Handling Stage3D Context Loss
Context loss with Stage3D
is real and will instantly take down your 3D scene. It’s an unavoidable problem, but one that many programmers still don’t handle gracefully. How does it occur? Why does it matter? How can you handle it so your app continues to function well? Today’s article explores the topic of Stage3D
context loss so you can keep your 3D scene alive and well.
At the start of any Stage3D
-based app you will need to request a Context3D
with which you will render your 3D scenes. The problem of context loss manifests itself when you suddenly lose your Context3D
. This can occur for a variety of reasons that are almost always outside of your app’s control. For example, pressing Ctrl+Alt+Delete on a Windows 7 computer will bring up a menu with options like “Lock this computer” and “Start Task Manager”. This also triggers a context loss. But this isn’t the only way to lose the context. Some screensavers starting up can trigger a context loss and so can closing the lid of a laptop. You can even simulate a context loss by calling Context3D.dispose
yourself. It’s best for most Flash apps to simply assume that context loss can strike at any moment for any reason and to handle it as best as possible.
What does it mean to your app when you lose the Context3D
? Well, you certainly can’t do any more rendering with your old Context3D
instance. This means that you won’t be able to render the 3D scene at all. Since it may be a while until you get a new context, you may wish to show some kind of “loading” or similar screen to indicate that there is a problem and your app is waiting for a new context.
Perhaps the much more complex and expensive problem is that all of the textures, vertex buffers, index buffers, and shader programs you created with the old Context3D
are also unusable. This means you’ll need to recreate all of these in order to restore your 3D scene. How you do this is up to you, but it often involves keeping a copy of the data that you uploaded to the GPU via a function like Texture.uploadFromBitmapData
. Unfortunately, this means that your app will use a lot of extra system memory just to be able to recover from context loss. You may want to consider alternative ways of recreating these resources such as re-downloading and then re-uploading assets or perhaps simply considering context loss a “fatal error” and restarting the app, game level, or some other reboot.
So how do you know that you’ve lost the Context3D
? If you’ve looked through the AS3 documentation you may have noticed that there doesn’t seem to be an event for this critical occurrence. Well, Flash Player’s way of letting you know is to simply dispatch another Event.CONTEXT3D_CREATE
event from the Stage3D
. This will often lead you to write some conditional code in your event handler that looks something like this:
var stage3D:Stage3D; var context:Context3D; // Request a context as part of starting up the Stage3D-based app stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreated); stage3D.requestContext3D(); function onContextCreated(ev:Event): void { // Save the old context so we can tell if this was the first context or a replacement // one due to context loss var oldContext:Context3D = context; context = stage3D.context3D; // create all textures, vertex buffers, index buffers, and shader programs and upload them... if (oldContext) { // had a context before // this means we lost it and got a new one } else { // getting a context is a normal part of app startup // we have one for the first time (i.e. during app startup) // so continue starting up the app... } }
You’ll have to decide for your specific app what the best way to handle context loss, but the above is a general strategy: (re-)create and (re-)upload all of the previously-used 3D resources whenever the context is created.
Another strategy is to abstract 3D resources from some parts of your app to allow you to more easily swap them out when you need to recreate them. Here’s some pseudo-code to illustrate:
class MyTexture { var bmd:BitmapData; var texture:Texture; } class MyScene { var textures:Vector.<MyTexture>; function onContextCreated(ev:Event): void { // Re-create and re-upload all textures for each (var tex:MyTexture in textures) { tex.texture = context.createTexture( tex.bmd.width, tex.bmd.height, Context3DTextureFormat.BGRA ); tex.texture.uploadFromBitmapData(tex.bmd); } } } class MyApp { // Users don't use Texture objects directly // Instead they use the middle-man: MyTexture var tex:MyTexture; function render(): void { context.setTextureAt(0, tex.texture); } }
Have any tips for handling context loss? Share them in the comments!
#1 by makc on January 1st, 2013 ·
I think starling uses context3D.driverInfo != “Disposed” check. This sometimes takes whole second, though. Probably a bug.
#2 by Zach Foley on January 8th, 2013 ·
Terriffic article on a tricky topic. Excellent work, Jackson!
#3 by benjamin guihaire on June 21st, 2013 ·
Great article,
my notes on context3d:
Context3d is lost when going full screen on chrome when the Google-PEPPER flash version is used.
Going to full screen with the other browser or with chrome with a non pepper flash version doesn’t cause a lost of context3d.
Context3d is lost on all browser when the computer goes to sleep,
Note that you can simulate the lost of a Context3d in your app to make sure you correctly support it by simply calling:
yourContext3D.dispose();
* I have never seen a Context3d loss on iOS with an AIR app.
Supporting lost of context can have a huge memory impact on your app or game.
And if your app use tons of memory , and memory is critical, one solution for browser apps to keep your memory usage down is to:
1)each time a texture is uploaded to GPU, and if you never need it again in ActionScript side, you can dispose the bitmap data (or clear the bytearray if AMF is used)
* same goes with indexBuffer and vertexBuffer, once uploaded to GPU, you can make sure the Vector. for the index buffer and Vector. gets deleted from memory.
Also, one way to make the index and vertex buffer use less memory is to use ByteArray instead (so it use 16 bits for index buffer instead of 32 bits, and 32bits float instead of 64bts Number for vertex Buffer)
2)in the lost of context event function : function onContextCreated(ev:Event): void ,
save the state of your app, force a refresh of your browser page,
3) Once your flash app starts again, reload the state of your app previously saved.
If the lost of context was caused by going to full screen (or back to normal screen mode)
(and you are most likely running pepper flash in that case), make sure to go to full screen (or back) before creating the context3D.
Benjamin Guihaire.
#4 by jackson on June 21st, 2013 ·
Great notes!
#5 by sebastien on July 9th, 2013 ·
On iOS, you will lose the context when using CameraUI.
#6 by Andrew Rapo on January 16th, 2014 ·
Jackson,
I am troubleshooting an issue that sounds like it could be a lost context, but I am not seeing the event indicating a new context has been created.
The issue occurs after playing a video with StageVideo (hide stage3D then reveal it when the video ends). When I call __stageVideo.attachNetStream(null) my stage3D goes completely black. And again, I don’t get the Event.CONTEXT3D_CREATE evet. It works fine on the Mac, but the issue shows up on an iPad2 using AIR 3.9 and Adobe’s GameSDK 1.2.
Have you encountered this before?
Thanks.
Andrew
#7 by jackson on January 16th, 2014 ·
Hey Andrew. I haven’t encountered that before, probably because I haven’t ever had cause to use
StageVideo
andStage3D
in the same app. It sounds like a bug though. Have you checked Adobe’s bug database? In any case, a black screen makes sense if you’re not getting a newCONTEXT3D_CREATE
event indicating aContext3D
has been created. Perhaps there is a workaround though. For example, during the video you could keep theStage3D
going, change yourContext3D.clear
calls to havealpha
set to0
, and simply not render anything (viaContext3D.drawTriangles
). That’s just one idea. I’m sure there are more, perhaps in the bug database.Hope that helps.
#8 by Andrew Rapo on January 16th, 2014 ·
Thanks!
I did just find this bug, which sounds similar, although I am experience this on iOS and stage3D is fine until StageVideo attempts to attach a null or new netstream.
https://bugbase.adobe.com/index.cfm?event=bug&id=3651239