OpenGL and Window Resizing

A recent convert to SDL has the following problem…
I’ve had trouble with window resizing. Unfortunately my engine uses lots
of display lists. I use SDL_SetVideoMode to resize the window (or switch
to fullscreen) but in the process the OpenGL context is destroyed and a
new one created. The problem is, this also destroys my display lists
(which reside in an octree). It takes 20 seconds or so on my 1.2GHz
machine to recreate the display lists, much longer on slower machines.

Does anyone know how I can change video modes without destroying the
OpenGL context? I know that might be impossible, so if not any other
tricks?

Can anyone shed some light on this problem?

Thanks,

Dom.

A recent convert to SDL has the following problem…
I’ve had trouble with window resizing. Unfortunately my engine uses lots
of display lists. I use SDL_SetVideoMode to resize the window (or switch
to fullscreen) but in the process the OpenGL context is destroyed and a
new one created. The problem is, this also destroys my display lists
(which reside in an octree). It takes 20 seconds or so on my 1.2GHz
machine to recreate the display lists, much longer on slower machines.

It also nukes textures.

Does anyone know how I can change video modes without destroying the
OpenGL context? I know that might be impossible, so if not any other
tricks?

I wrote a hack for the Windows implementation to allow changing some
window settings (everything except bit depth, I believe, which needs a
new context anyway), but only in Windows; I havn’t implemented it on
anything else yet.

I’ll attach it. It moves some stuff, so it’s a little large. The code
to use it looks like this:

#ifndef SDL_HAS_CHANGEVIDEOMODE
/* We can’t change the video mode without nuking the GL context. */
need_reload = true;
#endif

if( bpp != g_CurrentBPP )
	need_reload = true; /* can't do this with SDL_SM_ChangeVideoMode_OpenGL */ 

if(need_reload) {
	if(TEXTUREMAN) TEXTUREMAN->InvalidateTextures();
}

if(!g_screen || need_reload) {
	g_screen = SDL_SetVideoMode(width, height, bpp, g_flags);
	if(!g_screen)
		throw RageException("SDL_SetVideoMode failed: %s", SDL_GetError());

	SDL_WM_SetCaption("StepMania", "StepMania");
}

#ifdef SDL_HAS_CHANGEVIDEOMODE
else
{
SDL_SM_ChangeVideoMode_OpenGL(g_flags, g_screen, width, height);
}
#endif

You’ll need to massage the patch (remove the refresh rate stuff, add a
prototype and the SDL_HAS_CHANGEVIDEOMODE #define) if you want to use it.
(Sorry, I didn’t clean it up because diffing is a bit of a pain for me at
the moment …)

I think this is something the SDL really needs to handle; nuking the GL
context to change video settings is definitely not good. I’m not sure of
a good general-purpose way to handle it, though, considering that some
archs may not be able to do certain things without a context recreation.On Mon, Dec 09, 2002 at 08:01:14AM +1100, Dominique Louis wrote:


Glenn Maynard
-------------- next part --------------
diff -ur /home/glenn/SDL12/src/video/windx5/SDL_dx5video.c ./SDL_dx5video.c
— /home/glenn/SDL12/src/video/windx5/SDL_dx5video.c 2002-10-11 03:55:22.000000000 -0400
+++ ./SDL_dx5video.c 2002-12-08 16:16:04.000000000 -0500
@@ -22,7 +22,7 @@

#ifdef SAVE_RCSID
static char rcsid =

  • “@(#) $Id: SDL_dx5video.c,v 1.21 2002/10/11 07:55:22 slouken Exp $”;
  • “@(#) $Id: SDL_dx5video.c,v 1.1 2002/11/28 23:32:09 gmaynard Exp $”;
    #endif

#include <stdio.h>
@@ -41,6 +41,7 @@
#include “SDL_events.h”
#include “SDL_syswm.h”
#include “SDL_sysvideo.h”
+#include “SDL_RLEaccel_c.h”
#include “SDL_blit.h”
#include “SDL_pixels_c.h”
#include “SDL_dx5video.h”
@@ -972,6 +973,132 @@
static void DX5_WindowUpdate(_THIS, int numrects, SDL_Rect *rects);
static void DX5_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);

+extern int refresh_rate;
+
+void DX5_SetVideoMode_OpenGL(Uint32 flags, SDL_Surface *video, int width, int height, int was_visible)
+{

  •    DWORD style;
    
  • const DWORD directstyle =
  •   	(WS_POPUP);
    
  • const DWORD windowstyle =
  •   	(WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX);
    
  • const DWORD resizestyle =
  •   	(WS_THICKFRAME|WS_MAXIMIZEBOX);
    
  • /* Fill in part of the video surface */
  • video->flags = 0; /* Clear flags */
  • video->w = width;
  • video->h = height;
  • video->pitch = SDL_CalculatePitch(video);

+#ifndef NO_CHANGEDISPLAYSETTINGS

  • /* Set fullscreen mode if appropriate.
  •   Ugh, since our list of valid video modes comes from
    
  •   the DirectX driver, we may not actually be able to
    
  •   change to the desired resolution here.
    
  •   FIXME: Should we do a closest match?
    
  •   */
    
  • {
  •   DEVMODE settings;
    
  •   DWORD dwflags = 0;
    
  •   int ret;
    
  •   /* First, try to set or reset the fullscreen video mode. */
    
  •   memset(&settings, 0, sizeof(DEVMODE));
    
  •   settings.dmSize = sizeof(DEVMODE);
    
  •   if ( (flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) {
    
  •       settings.dmBitsPerPel = video->format->BitsPerPixel;
    
  •       settings.dmPelsWidth = width;
    
  •       settings.dmPelsHeight = height;
    
  •       settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
    
  •       dwflags = CDS_FULLSCREEN;
    
  •   }
    
  •   if(refresh_rate) {
    
  •   	settings.dmDisplayFrequency = refresh_rate;
    
  •   		settings.dmFields |= DM_DISPLAYFREQUENCY;
    
  •   }
    
  •   ret = ChangeDisplaySettings(&settings, dwflags);
    
  •   if (ret != DISP_CHANGE_SUCCESSFUL &&
    
  •       settings.dmFields & DM_DISPLAYFREQUENCY) {
    
  •   	/* We failed.  Try again without setting the refresh rate. */
    
  •   	settings.dmFields &= ~DM_DISPLAYFREQUENCY;
    
  •   	ret = ChangeDisplaySettings(&settings, dwflags);
    
  •   }
    
  •   if ( ret == DISP_CHANGE_SUCCESSFUL && (flags & SDL_FULLSCREEN)) {
    
  •   	video->flags |= SDL_FULLSCREEN;
    
  •   	SDL_fullscreen_mode = settings;
    
  •   }
    
  • }
    +#endif /* !NO_CHANGEDISPLAYSETTINGS */
  • style = GetWindowLong(SDL_Window, GWL_STYLE);
  • style &= ~(resizestyle|WS_MAXIMIZE);
  • if ( video->flags & SDL_FULLSCREEN ) {
  •   style &= ~windowstyle;
    
  •   style |= directstyle;
    
  • } else {
  •   if ( flags & SDL_NOFRAME ) {
    
  •   	style &= ~windowstyle;
    
  •   	style |= directstyle;
    
  •   	video->flags |= SDL_NOFRAME;
    
  •   } else {
    
  •   	style &= ~directstyle;
    
  •   	style |= windowstyle;
    
  •   	if ( flags & SDL_RESIZABLE ) {
    
  •   		style |= resizestyle;
    
  •   		video->flags |= SDL_RESIZABLE;
    
  •   	}
    
  •   }
    

+#if WS_MAXIMIZE

  •   if (IsZoomed(SDL_Window)) style |= WS_MAXIMIZE;
    

+#endif

  • }
  • SetWindowLong(SDL_Window, GWL_STYLE, style);
  • /* Resize the window (copied from SDL WinDIB driver) */
  • if ( SDL_windowid == NULL ) {
  •   HWND top;
    
  •   UINT swp_flags;
    
  •   RECT bounds;
    
  •   int x, y;
    
  •   SDL_resizing = 1;
    
  •   bounds.top    = 0;
    
  •   bounds.bottom = video->h;
    
  •   bounds.left   = 0;
    
  •   bounds.right  = video->w;
    
  •   AdjustWindowRectEx(&bounds, GetWindowLong(SDL_Window, GWL_STYLE), FALSE, 0);
    
  •   width = bounds.right-bounds.left;
    
  •   height = bounds.bottom-bounds.top;
    
  •   x = (GetSystemMetrics(SM_CXSCREEN)-width)/2;
    
  •   y = (GetSystemMetrics(SM_CYSCREEN)-height)/2;
    
  •   if ( y < 0 ) { /* Cover up title bar for more client area */
    
  •   	y -= GetSystemMetrics(SM_CYCAPTION)/2;
    
  •   }
    
  •   swp_flags = (SWP_NOCOPYBITS | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
    
  •   if ( was_visible && !(video->flags & SDL_FULLSCREEN) ) {
    
  •   	swp_flags |= SWP_NOMOVE;
    
  •   }
    
  •   if ( video->flags & SDL_FULLSCREEN ) {
    
  •   	top = HWND_TOPMOST;
    
  •   } else {
    
  •   	top = HWND_NOTOPMOST;
    
  •   }
    
  •   SetWindowPos(SDL_Window, top, x, y, width, height, swp_flags);
    
  •   SDL_resizing = 0;
    
  •   SetForegroundWindow(SDL_Window);
    
  • }
  • video->flags |= SDL_OPENGL;
    +}

+void SDL_SM_ChangeVideoMode_OpenGL(Uint32 flags, SDL_Surface *video, int width, int height)
+{

  • DX5_SetVideoMode_OpenGL(flags, video, width, height, 1);
    +}

SDL_Surface *DX5_SetVideoMode(_THIS, SDL_Surface *current,
int width, int height, int bpp, Uint32 flags)
{
@@ -1021,8 +1148,6 @@

/* If we are setting a GL mode, use GDI, not DirectX (yuck) */
if ( flags & SDL_OPENGL ) {
  •   RECT bounds;
    
  •   int x, y;
      Uint32 Rmask, Gmask, Bmask;
    
      /* Recalculate the bitmasks if necessary */
    

@@ -1065,96 +1190,13 @@
}
}

  •   /* Fill in part of the video surface */
    
  •   video->flags = 0;	/* Clear flags */
    
  •   video->w = width;
    
  •   video->h = height;
    
  •   video->pitch = SDL_CalculatePitch(video);
    

-#ifndef NO_CHANGEDISPLAYSETTINGS

  •   /* Set fullscreen mode if appropriate.
    
  •      Ugh, since our list of valid video modes comes from
    
  •      the DirectX driver, we may not actually be able to
    
  •      change to the desired resolution here.
    
  •      FIXME: Should we do a closest match?
    
  •    */
    
  •   if ( (flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) {
    
  •   	DEVMODE settings;
    
  •   	memset(&settings, 0, sizeof(DEVMODE));
    
  •   	settings.dmSize = sizeof(DEVMODE);
    
  •   	settings.dmBitsPerPel = video->format->BitsPerPixel;
    
  •   	settings.dmPelsWidth = width;
    
  •   	settings.dmPelsHeight = height;
    
  •   	settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
    
  •   	if ( ChangeDisplaySettings(&settings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL ) {
    
  •   		video->flags |= SDL_FULLSCREEN;
    
  •   		SDL_fullscreen_mode = settings;
    
  •   	}
    
  •   }
    

-#endif /* !NO_CHANGEDISPLAYSETTINGS */

  •   style = GetWindowLong(SDL_Window, GWL_STYLE);
    
  •   style &= ~(resizestyle|WS_MAXIMIZE);
    
  •   if ( video->flags & SDL_FULLSCREEN ) {
    
  •   	style &= ~windowstyle;
    
  •   	style |= directstyle;
    
  •   } else {
    
  •   	if ( flags & SDL_NOFRAME ) {
    
  •   		style &= ~windowstyle;
    
  •   		style |= directstyle;
    
  •   		video->flags |= SDL_NOFRAME;
    
  •   	} else {
    
  •   		style &= ~directstyle;
    
  •   		style |= windowstyle;
    
  •   		if ( flags & SDL_RESIZABLE ) {
    
  •   			style |= resizestyle;
    
  •   			video->flags |= SDL_RESIZABLE;
    
  •   		}
    
  •   	}
    

-#if WS_MAXIMIZE

  •   	if (IsZoomed(SDL_Window)) style |= WS_MAXIMIZE;
    

-#endif

  •   }
    
  •   SetWindowLong(SDL_Window, GWL_STYLE, style);
    
  •   /* Resize the window (copied from SDL WinDIB driver) */
    
  •   if ( SDL_windowid == NULL ) {
    
  •   	HWND top;
    
  •   	UINT swp_flags;
    
  •   	SDL_resizing = 1;
    
  •   	bounds.top    = 0;
    
  •   	bounds.bottom = video->h;
    
  •   	bounds.left   = 0;
    
  •   	bounds.right  = video->w;
    
  •   	AdjustWindowRectEx(&bounds, GetWindowLong(SDL_Window, GWL_STYLE), FALSE, 0);
    
  •   	width = bounds.right-bounds.left;
    
  •   	height = bounds.bottom-bounds.top;
    
  •   	x = (GetSystemMetrics(SM_CXSCREEN)-width)/2;
    
  •   	y = (GetSystemMetrics(SM_CYSCREEN)-height)/2;
    
  •   	if ( y < 0 ) { /* Cover up title bar for more client area */
    
  •   		y -= GetSystemMetrics(SM_CYCAPTION)/2;
    
  •   	}
    
  •   	swp_flags = (SWP_NOCOPYBITS | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
    
  •   	if ( was_visible && !(video->flags & SDL_FULLSCREEN) ) {
    
  •   		swp_flags |= SWP_NOMOVE;
    
  •   	}
    
  •   	if ( video->flags & SDL_FULLSCREEN ) {
    
  •   		top = HWND_TOPMOST;
    
  •   	} else {
    
  •   		top = HWND_NOTOPMOST;
    
  •   	}
    
  •   	SetWindowPos(SDL_Window, top, x, y, width, height, swp_flags);
    
  •   	SDL_resizing = 0;
    
  •   	SetForegroundWindow(SDL_Window);
    
  •   }
    
  •   DX5_SetVideoMode_OpenGL(flags, video, width, height, was_visible);
    
      /* Set up for OpenGL */
      if ( WIN_GL_SetupWindow(this) < 0 ) {
      	return(NULL);
      }
    
  •   video->flags |= SDL_OPENGL;
    
  •   return(video);
    
    }