Easily Get the Best Context3D
When you request a Context3D
you’ll either get the requested profile or software rendering. But what if you’d rather fall back to a lesser hardware context? What if your app can make use of the regular/baseline profile but can run with reduced graphical effects using the constrained mode profile? Today’s article presents a utility class that makes it a snap to always get the best Context3D
that Flash Player can give you.
The following class—GetContext3D
—works by trying to get an extended profile Context3D
so you can use the most features. If that fails, it falls back to the next-best profile until it either acquires one successfully or fails to get any Context3D
at all. Here’s the order it tries:
- Extended profile (hardware rendering)
- Regular/baseline profile (hardware rendering)
- Constrained mode (hardware rendering)
- Software rendering
The API is really simple. All you do is construct the class and it calls you back when the context is ready. Here’s a quick example:
package { import flash.display3D.Context3D; import flash.display.Sprite; import flash.display.Stage3D; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.utils.getTimer; /** * Test app to show how to use GetContext3D to get the best Stage3D profile * @author Jackson Dunstan, JacksonDunstan.com */ public class Stage3DProfileTest extends Sprite { /** * Application entry point */ public function Stage3DProfileTest() { // Disable classic stage scaling stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; // Get the best context we can new GetContext3D(stage.stage3Ds[0], onContext); } /** * Callback for when the context is created * @param success If the context was created successfully * @param message Mesage about the context creation */ private function onContext(success:Boolean, message:String): void { // Show a message text field var logger:TextField = new TextField(); logger.background = true; logger.backgroundColor = 0xffffffff; logger.autoSize = TextFieldAutoSize.LEFT; logger.text = message; addChild(logger); // The context was created successfully if (success) { // Get the context var stage3D:Stage3D = stage.stage3Ds[0]; var context3D:Context3D = stage3D.context3D; // Setup the back buffer var width:Number = stage.stageWidth; var height:Number = stage.stageHeight; context3D.configureBackBuffer(width, height, 0, true); // Show a message about what driver is being used logger.appendText("\nDriver: " + context3D.driverInfo); // Start rendering every frame addEventListener(Event.ENTER_FRAME, onEnterFrame); } // Failed to create the context else { logger.appendText("\nContext creation failed"); } } /** * Callback for when a frame should be rendered * @param ev ENTER_FRAME event */ private function onEnterFrame(ev:Event): void { // Just render a pulsating gray screen var context3D:Context3D = stage.stage3Ds[0].context3D; var gray:Number = (Math.sin(getTimer()/1000.0) + 1) / 2; context3D.clear(gray, gray, gray); context3D.present(); } } }
GetContext3D
is written in such a way that it has no dependencies and doesn’t even need you to compile your SWF for Flash Player 11.8 (extended profile support) or Flash Player 11.4 (constrained profile support). You can compile for any version of Flash Player starting at the first one to support Stage3D
and Context3D
at all: 11.0.
Here’s the code:
package { import flash.display.Stage3D; import flash.display3D.Context3DRenderMode; import flash.events.ErrorEvent; import flash.events.Event; /** * A simple way to get an optimal Context3D. That is, an extended profile * context is the first priority, then a baseline profile, then a * constrained profile, and finally a software profile. * @author Jackson Dunstan, JacksonDunstan.com */ public class GetContext3D { // Context3DProfile.BASELINE_EXTENDED. Use a string literal instead of // the Context3DProfile class so that this class can still be compiled // for Flash Players before the Context3DProfile class existed. private static const BASELINE_EXTENDED:String = "baselineExtended"; // Context3DProfile.BASELINE. Use a string literal instead of // the Context3DProfile class so that this class can still be compiled // for Flash Players before the Context3DProfile class existed. private static const BASELINE:String = "baseline"; // Context3DProfile.BASELINE_CONSTRAINED. Use a string literal instead of // the Context3DProfile class so that this class can still be compiled // for Flash Players before the Context3DProfile class existed. private static const BASELINE_CONSTRAINED:String = "baselineConstrained"; /** Profile to get */ private var profile:String; /** Render mode to get */ private var renderMode:String; /** Stage3D to get the context for */ private var stage3D:Stage3D; /** Callback to call when the context is acquired or an error occurs * that prevents the context from being acquired. Passed a success * Boolean and a message String if the callback function takes two or * more parameters. If the callback function takes only one parameter, * only the success Boolean is passed. If the callback function takes * no parameters, none are passed. */ private var callback:Function; /** * Get the best Context3D for a Stage3D and call the callback when done * @param stage3D Stage3D to get the context for * @param callback Callback to call when the context is acquired or an * error occurs that prevents the context from being acquired. * Passed a success Boolean and a message String if the callback * function takes two or more parameters. If the callback * function takes only one parameter, only the success Boolean is * passed. If the callback function takes no parameters, none are * passed. * @throws Error If the given stage3D or callback is null */ public function GetContext3D(stage3D:Stage3D, callback:Function) { // Callback and Stage3D must be non-null if (callback == null) { throw new Error("Callback can't be null") } if (stage3D == null) { throw new Error("Stage3D can't be null") } // Save the callback and Stage3D this.callback = callback; this.stage3D = stage3D; // Start by trying to acquire the best kind of profile renderMode = Context3DRenderMode.AUTO; profile = BASELINE_EXTENDED; stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreated); stage3D.addEventListener(ErrorEvent.ERROR, onStage3DError); requestContext(); } /** * Call the callback function and clean up * @param success If the context was acquired successfully * @param message Message about the context acquisition */ private function callCallback(success:Boolean, message:String): void { // Remove event listeners stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContext3DCreated); stage3D.removeEventListener(ErrorEvent.ERROR, onStage3DError); // Release reference to the callback after this function completes var callback:Function = this.callback; this.callback = null; // Release reference to the Stage3D stage3D = null; // Pass as many arguments as the callback function requires var numArgs:uint = callback.length; switch (numArgs) { case 0: callback(); break; case 1: callback(success); break; case 2: callback(success, message); break; } } /** * Request a context with the current profile */ private function requestContext(): void { try { // Only pass a profile when the parameter is accepted. Do this // dynamically so we can still compile for older versions. if (stage3D.requestContext3D.length >= 2) { stage3D["requestContext3D"](renderMode, profile); } else { stage3D.requestContext3D(renderMode); } } catch (err:Error) { // Failed to acquire the context. Fall back. fallback(); } } /** * Callback for when there is an error creating the context * @param ev Error event */ private function onStage3DError(ev:ErrorEvent): void { callCallback(false, "Stage3D error ID: " + ev.errorID + "\n" + ev); } /** * Callback for when the context is created * @param ev CONTEXT3D_CREATE event */ private function onContext3DCreated(ev:Event): void { // Got a software driver var driverInfo:String = stage3D.context3D.driverInfo.toLowerCase(); var gotSoftware:Boolean = driverInfo.indexOf("software") >= 0; if (gotSoftware) { // Trying to get a non-software profile if (renderMode == Context3DRenderMode.AUTO) { fallback(); } else { // Trying to get software. Succeeded. callCallback(true, "Profile: " + profile); } } // Didn't get a software driver else { callCallback(true, "Profile: " + profile); } } /** * Fall back to the next-best profile */ private function fallback(): void { // Check what profile we were trying to get switch (profile) { // Trying to get extended profile. Try baseline. case BASELINE_EXTENDED: profile = BASELINE; requestContext(); break; // Trying to get baseline profile. Try constrained. case BASELINE: profile = BASELINE_CONSTRAINED; requestContext(); break; // Trying to get constrained profile. Try software. case BASELINE_CONSTRAINED: profile = BASELINE; renderMode = Context3DRenderMode.SOFTWARE; requestContext(); break; } } } }
Hopefully this makes your Context3D
acquisition easier and results in a context with more features.
Spot a bug? Have a question or suggestion? Post a comment!
#1 by Taaniel on December 31st, 2013 ·
I just read Flash API about different profiles and was thinking, how do you know, what is the best profile available. I think, this is very neat way. Well written as always. Happy new year!
#2 by phendrax on January 6th, 2014 ·
Hi, nice article and thanks for the GetContext3D class :)
I think you should call callCallback() in onStage3DError() instead of callback()
#3 by jackson on January 6th, 2014 ·
Thanks for spotting the typo. I’ve updated the article with your suggested fix.
#4 by mrchnk on February 25th, 2014 ·
Hi, i have tested your class on my old macbook and figure out some strange behavior:
if i got onStage3DError with baselineExtended, but i could get baseline context if i request it first
so if i put fallback instead of callback in onStage3DError i could find my context, but i could not request context on the same frame, so i put fallback in setTimeout
my version of GetContext3D
https://gist.github.com/mrchnk/9225203
some traces
https://gist.github.com/mrchnk/9225152