Reloading images after switching to fullscreen
Monkey Targets Forums/Desktop/Reloading images after switching to fullscreen
| ||
I've been messing around with creating external functions to switch between windowed and fullscreen mode in GLFW, and I ran into a problem. If I close the window, create a new one and attach the callbacks again everything runs fine...except for the fact that the images are lost. Is there some way to force a reload of the images? I'm using Diddy, and I've tried different things like: * Running "game.images.Clear()" and then reload all the images. No change. * Patched mojo.app so that I could run "SetGraphicsContext New GraphicsContext( GraphicsDevice() )" as well, but that crashes the game. * Tried to simply create a new instance of my DiddyApp to refresh everything, but that also crashes. Instead of trying different longshots I might as well check if anyone has any hints... At the moment I can do a switch to fullscreen/windowed before I have loaded the images, so I can do a "Please restart the game for this change to take effect" at least. The c++ code for the functions: static void setWindowed(int w, int h) { GLFWvidmode desktopMode; glfwGetDesktopMode( &desktopMode ); glfwCloseWindow(); glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_TRUE ); glfwOpenWindow( w, h, 0, 0, 0, 0, 0, 0, GLFW_WINDOW ); glfwSetWindowPos( (desktopMode.Width-w)/2,(desktopMode.Height-h)/2 ); glfwEnable( GLFW_KEY_REPEAT ); glfwDisable( GLFW_AUTO_POLL_EVENTS ); glfwSetKeyCallback( gxtkApp::OnKey ); glfwSetCharCallback( gxtkApp::OnChar ); glfwSetWindowSizeCallback( gxtkApp::OnWindowSize ); glfwSetWindowRefreshCallback( gxtkApp::OnWindowRefresh ); glfwSetMouseButtonCallback( gxtkApp::OnMouseButton ); } static void setFullscreen() { GLFWvidmode desktopMode; glfwGetDesktopMode( &desktopMode ); glfwCloseWindow(); glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_TRUE ); glfwOpenWindow( desktopMode.Width, desktopMode.Height, 0, 0, 0, 0, 0, 0, GLFW_FULLSCREEN ); glfwSetWindowPos( 0, 0 ); glfwEnable( GLFW_KEY_REPEAT ); glfwDisable( GLFW_AUTO_POLL_EVENTS ); glfwSetKeyCallback( gxtkApp::OnKey ); glfwSetCharCallback( gxtkApp::OnChar ); glfwSetWindowSizeCallback( gxtkApp::OnWindowSize ); glfwSetWindowRefreshCallback( gxtkApp::OnWindowRefresh ); glfwSetMouseButtonCallback( gxtkApp::OnMouseButton ); glfwEnable( GLFW_MOUSE_CURSOR ); } |
| ||
You have to reload the images. Mojo dumps the data after uploading into video memory and if you're creating a new window, then you'll get a new opengl context. You might need to use a flag (reload_images=true) to do the actual loading of images within OnRender(). I know I had to do this in android. |
| ||
Yeah, I suspected as much - but how do I do that with diddy? I cleared the "game.images" list and re-loaded all the data with "game.images.Load()" etc. But there's no change - the image data is still gone. I also tried explicitly clearing each image before reloading, but the Discard() call just crashes the app: Local g:GameImage = game.images.Get( StripAll( i.name.ToUpper() ) ) If( g <> Null ) g.image.Discard() game.images.Remove( StripAll( i.name ) ) Endif Edit: Oh, right - maybe the context hasn't been initialized yet at the time when I try to reload the images. I'll try your example, with the reload flag that does it on OnRender instead. Thanks! |
| ||
Cool! I'll have to set this :) Do you mind if I add this to Diddy? When loading resources into the banks (images/sounds), we could keep a list of the loaded resources, so the user doesnt have to reload them themselves after going to fullscreen... maybe ;) |
| ||
That sounds like a great idea! And please, just go ahead and use this. Maybe I could help with modifying the banks as well, if time permits. (I'm getting a spontaneous thought that it might be a good idea to let the user trigger the reloading at will, to have time to show a loading screen before locking up everything for a while.) |
| ||
Hmmm, I've been messing around with this for a few hours... I think the memory address is totally lost when we are changing context. For example: background = game.images.Find("background") Points to background, but when we change context background is no longer there... even if we reload background, it might not use the same memory address. I've had my game crash with "Memory access violation" and alot of white squares... So how do we save the memory address to reload the images into the same address? |
| ||
Okay, I'm getting a bit lost here. I went for the approach to try to keep the GameImage structures intact, and simply reload the image after switching to windowed/fullscreen. So I modified Diddy's framework.monkey:Class ImageBank Extends StringMap<GameImage> Method Reload:GameImage(name:String, nameoverride:String = "", midhandle:Bool=True, ignoreCache:Bool=False) Local storeKey:String = nameoverride.ToUpper() If storeKey = "" Then storeKey = StripAll(name.ToUpper()) If Self.Contains(storeKey) Local i:GameImage = Self.Get(storeKey) i.Load(path + name, midhandle) Print "loaded " + name Return i End Return Null End In my loading function I did a check to see whether or not the GameImage structure was still present: g = game.images.Get( StripAll( i.name.ToUpper() ) ) If( g <> Null ) game.images.Reload( i.name, "", i.mid ) Else game.images.Load( i.name, "", i.mid ) Endif (Note: for me the game.images bank still seems to retain the GameImage data after I've switched to fullscreen... It's just the image data itself that needs to be reloaded and uploaded to video memory (like AdamRedwoods said above).) The result of this is that Load is called the first time, and on successive calls Reload is called instead, and i.Load() in turn is called inside the ImageBank. But it still won't work - I get white squares despite that... I tried reloading everything immediately after switching resolution, but I also tried to add a "reload stuff" flag so that it happened in my Render() method instead... But I still won't get the image data. Tracing the functions forward gives: GameImage::Load() -> LoadBitmap() -> LoadImage() -> Image::Load() -> gxtkGraphics::LoadSurface() And I don't really see anything that should be a problem there. Weird. :/ |
| ||
Yep, I tried multiple things like this... I even had it working for the first time I switch resolution but after that I got white squares... I might try and look in the BlitzMax's source to see how that does it, but I'm not that good with this low level coding... Functions.monkey: ... Function SetNativeGraphicsSize:Void(w:Int, h:Int, fullScreen:Int) = "diddy::setGraphics" ... Global reloadResources:Bool = False Function SetGraphics:Void(w:Int, h:Int, fullScreen:Int = 0) SetNativeGraphicsSize(w, h, fullScreen) DEVICE_WIDTH = w DEVICE_HEIGHT = h SCREEN_HEIGHT = h SCREEN_WIDTH = w SCREEN_WIDTH2 = SCREEN_WIDTH / 2 SCREEN_HEIGHT2 = SCREEN_HEIGHT / 2 reloadResources = True 'ReLoadResources() End Function ReLoadResources:Void() Print "reload" Local fnt:Image = LoadImage("mojo_font.png", 96, Image.XPadding) SetFont(fnt) For Local key:String = Eachin game.images.Keys() ' Print "key = "+key game.images.Get(key).image.Discard() Next game.images.Clear()' = New ImageBank Local rList:List<Resource> = New List<Resource> For Local r:Resource = Eachin resourcesList rList.AddLast(r) Next resourcesList.Clear() Local tmpImage:Image For Local r:Resource = Eachin rList Select r.type Case "image" Print "IMAGE..." Print "filename = "+r.fileName Print "override = "+r.overrideName If r.midhandle Print "midhangle= true" Else Print "midhangle= false" Endif game.images.Load(r.fileName, r.overrideName, r.midhandle) Case "animimage" Print "ANIMIMAGE..."+r.fileName +" "+r.type game.images.LoadAnim(r.fileName, r.width, r.height, r.frames, tmpImage, r.midhandle, False, r.overrideName) End Next For Local key:String = Eachin game.images.Keys() Print "new key = "+key 'game.images.Get(key).image.Discard() Next End framework.monkey: Method OnRender:Int() If reloadResources #if TARGET="glfw" ReLoadResources() #end reloadResources = False Return 0 End FPSCounter.Update() ... diddy.glfw.cpp static void setGraphics(int w, int h, int fullScreen) { if (fullScreen == 0) { GLFWvidmode desktopMode; glfwGetDesktopMode( &desktopMode ); glfwCloseWindow(); glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_TRUE ); glfwOpenWindow( w, h, 0, 0, 0, 0, 0, 0, GLFW_WINDOW ); glfwSetWindowPos( (desktopMode.Width-w)/2,(desktopMode.Height-h)/2 ); glfwEnable( GLFW_KEY_REPEAT ); glfwDisable( GLFW_AUTO_POLL_EVENTS ); glfwSetKeyCallback( gxtkApp::OnKey ); glfwSetCharCallback( gxtkApp::OnChar ); glfwSetWindowSizeCallback( gxtkApp::OnWindowSize ); glfwSetWindowRefreshCallback( gxtkApp::OnWindowRefresh ); glfwSetMouseButtonCallback( gxtkApp::OnMouseButton ); } else { GLFWvidmode desktopMode; glfwGetDesktopMode( &desktopMode ); glfwCloseWindow(); glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_TRUE ); glfwOpenWindow( w, h, 0, 0, 0, 0, 0, 0, GLFW_FULLSCREEN ); glfwSetWindowPos( 0, 0 ); glfwEnable( GLFW_KEY_REPEAT ); glfwDisable( GLFW_AUTO_POLL_EVENTS ); glfwSetKeyCallback( gxtkApp::OnKey ); glfwSetCharCallback( gxtkApp::OnChar ); glfwSetWindowSizeCallback( gxtkApp::OnWindowSize ); glfwSetWindowRefreshCallback( gxtkApp::OnWindowRefresh ); glfwSetMouseButtonCallback( gxtkApp::OnMouseButton ); glfwEnable( GLFW_MOUSE_CURSOR ); } Sorry for the messy code... ;) |
| ||
Thanks for the code update - I'll continue experimenting with this tomorrow, and maybe I can find something... Hah, this is pretty fun detective work. :) |
| ||
I have tried some different things such as adding glfwTerminate and Init when switching mode:glfwCloseWindow(); glfwTerminate(); if( !glfwInit() ){ puts( "glfwInit failed" ); exit( -1 ); } glfwOpenWindowHint( GLFW_WINDOW_NO_RESIZE, GL_TRUE ); No change. Tried adding a "glEnable( GL_TEXTURE_2D );" after opening the new window as well. Double-checked that there's no OpenGL error after glTexSubImage2D() in gxtkSurface *gxtkGraphics::LoadSurface(): glTexSubImage2D( GL_TEXTURE_2D,0,0,0,width,height,fmt,GL_UNSIGNED_BYTE,data ); if( glGetError()!=GL_NO_ERROR ){ glDeleteTextures( 1,&texture ); unloadImage( data ); return 0; } ...And in general checked that there are no errors in Image::Load, and that the width and height of the reloaded are correct. I'm starting to think that the loading of the data is going just fine, but something in OpenGL is buggy. E.g. that glTexImage2D() or glTexSubImage2D() is not working as expected, on the second time that a GL context is created. I also found some hints that some people with SDL have the same problem - but no solution: [url]http://www.gamedev.net/topic/557497-texture-reloading-when-toggling-fullscreen-sdlopengl/[/url] Also, after browsing the BlitzMax implementation for a little bit it seems that it uses straight OpenGL that doesn't have GLFW's limitations - there doesn't seem to be a need to reload the textures there. If this wasn't a family-friendly forum I'd have some choice words right now... |
| ||
I havent done anything with this tonight, but found these links: http://www.gamedev.net/topic/372906-glfw----change-video-mode----fullscreenwindowedresolution/ http://www.gamedev.net/topic/589777-toggle-fullscreen-with-glfw/ And going off the first one, it looks like we are trying to do it right... so we must be missing something :/ And I've found this: http://www.philipppixel.de/thegame/TheGameDoc/html/files.html Looks like a full game using GLFW and in the Start.cpp is a method to switch between full-screen and window mode... will need some time to go thru the code (ewwwwww C++) Also found this: GLOOT, a modified version of GLFW which doesnt lose its context when switching to full-screen: http://code.google.com/p/open-game-libraries/wiki/LibraryGloot |
| ||
Did anyone figure this out? Still can't switch graphics fullscreen on the fly? |
| ||
Sounds like it's a pretty important feature to have in order to not have to use BlitzMax. |
| ||
From what I understand GLFW needs to be updated to support this, I do remember reading a roadmap of GLFW saying they were going to add this... but then Monkey would need to be changed to use that version. http://wiki.glfw.org/wiki/Roadmap_for_GLFW Status of GLFW 3.x Window mode switching But even if GFLW had this, it would still only be OpenGL... |
| ||
Oh that's also a bit of problem too I guess. So really we need a DX target for Monkey if we want to do everything in monkey instead of using BMax for desktop games. |
| ||
http://code.google.com/p/monkey-max/wiki/MonkeyMax Grey, incase you didn't already know this, you can export to blitzmax if you need a dx target. |
| ||
Max has an interesting function call GLShrareContexts(), which i used in Scoregasm so i didn't need to reload all the images on a fullscreen/window flick. Might be worth a look... Cheers Charlie |
| ||
I cracked this mofo! http://www.skn3.com/junk/monkey/screen_mode_toggle.zip So all you have to do is make sure you call image.Discard() on all images before changing screen mode. The trouble is this is not possible to do automatically without an image manager. Even with an image manager, 3rd party modules and the built in font system will break. I have emailed mark for any suggestions and will post a module once I hear back. The source for the demo above is here: Import mojo Import skn3.funcs.graphics Import skn3.imagecache Function Main:Int() New Demo Return 0 End Class Demo Extends App Field fullScreen:= False Field image:ImageCache Method OnCreate:Int() ' --- app is created --- SetUpdateRate(60) image = LoadImageCache("monkey1.png") Return 0 End Method Method OnUpdate:Int() ' --- app is updated --- If KeyHit(KEY_SPACE) FreeImageSources(True) GetFont().Discard() If fullScreen fullScreen = False SetGraphics(800, 600, fullScreen) Else fullScreen = True SetGraphics(1024, 768, fullScreen) EndIf SetFont(LoadImage("mojo_font.png", 96, Image.XPadding)) 'reload the images ReloadImageSources() EndIf Return 0 End Method Method OnRender:Int() ' --- app is rendered --- Cls(0, 0, 0) DrawImageCache(image, 50, 50) DrawText("press space to toggle screen mode", 5, 5) Return 0 End Method End It is using an image cache module I wrote, but you can see the CLUDGE fix for the font reloading. |
| ||
Skn3: Sounds promising, nice one! I do have an image manager in my framework so it could work. Yeah I do know about MonkeyMax but I wasn't convinced it would be a smooth transition. |
| ||
Ok it seems the issue will have to be looked at again in monkey v67 but for now here is the code: http://www.monkeycoder.co.nz/Community/posts.php?topic=4418 You will have to manually discard and load any images. |