Retained Mode Considered Harmful
I think I've made a small step towards figuring out how drawing works in Flex. Flex's drawing system appears to use a "retained mode" approach, in which the calls you make to "draw stuff" don't actually draw stuff. Instead, they (in effect) define a (new, hidden, secret, inaccessible) method which Flash/Flex uses to draw stuff whenever it feels like it.
Here's a tiny little test applet, called "bar" (because "foo" was already taken):
The above applet draws a small black circle that slides across the applet from left to right, eventually leaving the stage. No biggie...but it's drawing, which is big progress for me. ;-)
Here's a link to the above applet's source code, bar.as:
www.igetitmusic.com/blog/SWFs/bar/srcview/index.html
This source code is a minor variation of recipe 11.1, "Moving an Object," from O'Reilly's ActionScript 3.0 Cookbook.
In bar.as, a class bar is declared to extend Flash's Sprite class. (Yes, I know that bar should, by ActionScript/Flex convention, be capitalized as Bar, because it is the name of a class. Gimme a break. It's not germane.)
Every bone in my body tells me that this code is INSANE. All of the drawing code is in bar's CONSTRUCTOR, fer cryin' out loud. How can this code be *found*, let alone called, when an instance of bar is to be drawn?
I would expect that this drawing code would be placed in a draw() method of the bar class (i.e., bar.draw()), which would be called by Flash whenever an instance of bar was to be drawn, and that this draw() method would be an override of a draw() method inherited, ultimately, from Flash's DisplayObject class (from which bar's superclass, Sprite, descends). But neither DisplayObject nor Sprite have a draw() method, or anything similar to a draw() method. (They do have a RENDER event, but it is used to advise an object that it is about to be rendered; it is NOT a request that the receiving object now render itself.)
It gets weirder. Every time the Flash player needs to draw a new animation frame, it dispatches an ENTER_FRAME event. The bar class listens for this message in its onEnterFrame() method. No problems there. To animate the circle rightwards, I would expect bar.onEnterFrame() to (a) shift the location of the bar instance slightly rightward, and then (b) call bar.draw().
But nooooo! As I mentioned above, bar has nothing like a draw() method, nor does any of its super-classes. The only thing that bar.onEnterFrame() does -- and remember, I stole this code *directly* from the published, well-regarded recipe mentioned above -- is offset the bar object slightly rightward (more on that later). That's it. There's no drawing code in bar.onEnterEvent() at all, nor any calls to any other drawing code.
Somehow, the Flash player is accessing the drawing code that's defined in bar's constructor, and executing it sometime after bar.onEnterFrame() method is called.
WTF?
I see this pattern all over the sample code for Flash & Flex, from many different authors. Adobe's online documentation for Flex 3 is rife with it. They put drawing code in the class' constructor, or into a function which is only ever called by the class' constructor (to verify this, I've set breakpoints on such drawing functions, and seen that they are never called from anywhere except the constructor).
HOW DOES THE FLASH PLAYER FIND THIS DRAWING CODE, in order to execute it when it's time to actually draw the object?
I'm not sure, but I think I have identified a clue: "retained mode."
Apparently, the Flash player (and hence ActionScript and Flex) uses "retained mode" for all drawing. Unfortunately, only initiates into Adobe's secret society are taught the secrets of "retained mode." It's not documented anywhere that I've been able to find.
The Wikipedia entry for "retained mode" is cryptic. The phrase "retained mode" does not occur in Adobe's online documentation for Flash, Flex, ActionScript, or Flex Builder. It's a big secret, and you can't really understand drawing in Flash/Flex/ActionScript unless you understand the Secret of Retained Mode. Or, at least, *I* can't. (Maybe I'm just stupid.)
Sure, I could just mindlessly copy other people's sample code, and put all of my drawing code into my classes' constructors. What the heck, it seems to work, right?
However, I've never been able to do that kind of "thought-free programming." It's one thing to reuse a well-documented component without understanding its implementation; that's what components (and their interfaces) are for. It is quite another to re-use a pattern without understanding it; that's a disaster waiting to happen. I want to understand. That makes me a slower "production" programmer initially, but eventually makes me more efficient and creative, because I come to understand how everything works, which allows me to make new and (hopefully) useful insights.
This Cartesian refusal to accept anything that one doesn't understand is *the* fundamental basis of all scientific advancement, fer cryin' out loud, so I'd think Adobe -- which had a strongly scientific culture, last time I interacted with it -- would give scientific skepticism a bit more respect by (for example) documenting its platform's architecture more clearly.
Here's what I think is going on deep in the secret bowels of Flash Player.
All "drawing" is effected by interacting with an instance of Flash's Graphics class. For example, in bar's constructor, the following code is called to effect the drawing of a circle:
this.graphics.beginFill(0x0000f, 100);
this.graphics.drawCircle(0, 0, 5);
this.graphics.endFill();
"this" is the instance of the bar class being constructed. Its graphics property is inherited from Sprite, which is bar's superclass.
Apparently, when you call the method of the graphics property -- such as the calls to beginFill(), drawCircle(), and endFill() above -- the methods don't actually draw anything. The method calls are simply recorded by the graphics object -- that is, they are retained. These method calls are (in effect) defining an implicit bar.draw() method (via delegation to bar.graphics), which Flash then calls whenever it needs to draw a bar instance.
In the bar.onEnterFrame() method, this.x is incremented by delta (initialized to 1 in bar's constructor).
this.x += delta;
Notice that incrementing this.x does not involve this.graphics, so it isn't "drawing code," per se. That is, it doesn't say what to draw, it just says where to draw.
Sometime after this.x is incremented in bar.onEnterFrame(), Flash calls the implicit (secret, hidden, retained) bar.draw() method that it generated from bar's calls to this.graphics's methods.
That is, I think, how drawing happens in Flash/Flex/ActionScript. If this is wrong in any significant way, please let me know.
If this is how it works, then it strikes me as being bad, bad, bad.
Retained mode is an example of self-modifying code, which always makes me nervous. It smells bad. Specifically, retained mode's implicit generation of a (secret, hidden, inaccessible) bar.draw() method makes it impossible to use time-honored object-oriented programming practices to modify a class' drawing behavior. In brief: I can't override a secret, hidden, retained draw() method.
I mean, really: drawing code in the constructor? The single-least-over-rideable method of any class? Are you insane?
I *must* be missing something, here. That just *can't* be right. I hope that someone will point out my misunderstanding and clarify how Flash player's drawing really works.
The opposite of retained mode is "immediate mode," in which calling the function drawCircle() draws a circle, right now. The Mac OS/Toolbox API, the Win32 API, the MacApp framework, the MFC framework -- every API I've ever worked with used immediate mode.
It is only by merest chance that I have any awareness of "retained mode" at all. I was on Microsoft's DirectX evangelism team back in the mid-1990's when Direct3D was first introduced. The first version of Direct3D used a retained mode API to improve efficiency (probably the same reason why Flash uses it). However, Direct3D's use of retained mode confused the heck out of game developers. As a result, they simply refused to use Direct3D, choosing to use OpenGL instead. The next version of Direct3D deprecated retained mode in favor of immediate mode, but it had lost so much mindshare in the meantime that it was a decade before Direct3D finally won over OpenGL's most vocal proponents.
Maybe all of this has changed since I stopped actively programming in 1992. Maybe now all Computer Science students are taught to think in terms of retained mode, so Adobe doesn't need to explain it anymore. Or maybe Adobe doesn't want to explain it; perhaps Adobe has too darn many ISVs already, and it wants to keep out the riff-raff by ensuring that only those who already know the secret "retained mode" handshake can get past the gate. That would be remarkably self-defeating, and although such self-defeating actions are surprisingly common, it doesn't sound like Adobe at all, but...what do I know?
Or maybe I'm barking up the wrong tree entirely, and Flash/Flex/ActionScript's drawing system doesn't used retained mode at all...in which case I'll apologize profusely.
Here's a tiny little test applet, called "bar" (because "foo" was already taken):
The above applet draws a small black circle that slides across the applet from left to right, eventually leaving the stage. No biggie...but it's drawing, which is big progress for me. ;-)
Here's a link to the above applet's source code, bar.as:
www.igetitmusic.com/blog/SWFs/bar/srcview/index.html
This source code is a minor variation of recipe 11.1, "Moving an Object," from O'Reilly's ActionScript 3.0 Cookbook.
In bar.as, a class bar is declared to extend Flash's Sprite class. (Yes, I know that bar should, by ActionScript/Flex convention, be capitalized as Bar, because it is the name of a class. Gimme a break. It's not germane.)
Every bone in my body tells me that this code is INSANE. All of the drawing code is in bar's CONSTRUCTOR, fer cryin' out loud. How can this code be *found*, let alone called, when an instance of bar is to be drawn?
I would expect that this drawing code would be placed in a draw() method of the bar class (i.e., bar.draw()), which would be called by Flash whenever an instance of bar was to be drawn, and that this draw() method would be an override of a draw() method inherited, ultimately, from Flash's DisplayObject class (from which bar's superclass, Sprite, descends). But neither DisplayObject nor Sprite have a draw() method, or anything similar to a draw() method. (They do have a RENDER event, but it is used to advise an object that it is about to be rendered; it is NOT a request that the receiving object now render itself.)
It gets weirder. Every time the Flash player needs to draw a new animation frame, it dispatches an ENTER_FRAME event. The bar class listens for this message in its onEnterFrame() method. No problems there. To animate the circle rightwards, I would expect bar.onEnterFrame() to (a) shift the location of the bar instance slightly rightward, and then (b) call bar.draw().
But nooooo! As I mentioned above, bar has nothing like a draw() method, nor does any of its super-classes. The only thing that bar.onEnterFrame() does -- and remember, I stole this code *directly* from the published, well-regarded recipe mentioned above -- is offset the bar object slightly rightward (more on that later). That's it. There's no drawing code in bar.onEnterEvent() at all, nor any calls to any other drawing code.
Somehow, the Flash player is accessing the drawing code that's defined in bar's constructor, and executing it sometime after bar.onEnterFrame() method is called.
WTF?
I see this pattern all over the sample code for Flash & Flex, from many different authors. Adobe's online documentation for Flex 3 is rife with it. They put drawing code in the class' constructor, or into a function which is only ever called by the class' constructor (to verify this, I've set breakpoints on such drawing functions, and seen that they are never called from anywhere except the constructor).
HOW DOES THE FLASH PLAYER FIND THIS DRAWING CODE, in order to execute it when it's time to actually draw the object?
I'm not sure, but I think I have identified a clue: "retained mode."
Apparently, the Flash player (and hence ActionScript and Flex) uses "retained mode" for all drawing. Unfortunately, only initiates into Adobe's secret society are taught the secrets of "retained mode." It's not documented anywhere that I've been able to find.
If you know where Adobe's use of retained mode is explained in detail, please
let me know where I can find this documentation.
The Wikipedia entry for "retained mode" is cryptic. The phrase "retained mode" does not occur in Adobe's online documentation for Flash, Flex, ActionScript, or Flex Builder. It's a big secret, and you can't really understand drawing in Flash/Flex/ActionScript unless you understand the Secret of Retained Mode. Or, at least, *I* can't. (Maybe I'm just stupid.)
Sure, I could just mindlessly copy other people's sample code, and put all of my drawing code into my classes' constructors. What the heck, it seems to work, right?
However, I've never been able to do that kind of "thought-free programming." It's one thing to reuse a well-documented component without understanding its implementation; that's what components (and their interfaces) are for. It is quite another to re-use a pattern without understanding it; that's a disaster waiting to happen. I want to understand. That makes me a slower "production" programmer initially, but eventually makes me more efficient and creative, because I come to understand how everything works, which allows me to make new and (hopefully) useful insights.
This Cartesian refusal to accept anything that one doesn't understand is *the* fundamental basis of all scientific advancement, fer cryin' out loud, so I'd think Adobe -- which had a strongly scientific culture, last time I interacted with it -- would give scientific skepticism a bit more respect by (for example) documenting its platform's architecture more clearly.
Here's what I think is going on deep in the secret bowels of Flash Player.
All "drawing" is effected by interacting with an instance of Flash's Graphics class. For example, in bar's constructor, the following code is called to effect the drawing of a circle:
this.graphics.beginFill(0x0000f, 100);
this.graphics.drawCircle(0, 0, 5);
this.graphics.endFill();
"this" is the instance of the bar class being constructed. Its graphics property is inherited from Sprite, which is bar's superclass.
Apparently, when you call the method of the graphics property -- such as the calls to beginFill(), drawCircle(), and endFill() above -- the methods don't actually draw anything. The method calls are simply recorded by the graphics object -- that is, they are retained. These method calls are (in effect) defining an implicit bar.draw() method (via delegation to bar.graphics), which Flash then calls whenever it needs to draw a bar instance.
In the bar.onEnterFrame() method, this.x is incremented by delta (initialized to 1 in bar's constructor).
this.x += delta;
Notice that incrementing this.x does not involve this.graphics, so it isn't "drawing code," per se. That is, it doesn't say what to draw, it just says where to draw.
Sometime after this.x is incremented in bar.onEnterFrame(), Flash calls the implicit (secret, hidden, retained) bar.draw() method that it generated from bar's calls to this.graphics's methods.
That is, I think, how drawing happens in Flash/Flex/ActionScript. If this is wrong in any significant way, please let me know.
If this is how it works, then it strikes me as being bad, bad, bad.
Retained mode is an example of self-modifying code, which always makes me nervous. It smells bad. Specifically, retained mode's implicit generation of a (secret, hidden, inaccessible) bar.draw() method makes it impossible to use time-honored object-oriented programming practices to modify a class' drawing behavior. In brief: I can't override a secret, hidden, retained draw() method.
I mean, really: drawing code in the constructor? The single-least-over-rideable method of any class? Are you insane?
I *must* be missing something, here. That just *can't* be right. I hope that someone will point out my misunderstanding and clarify how Flash player's drawing really works.
The opposite of retained mode is "immediate mode," in which calling the function drawCircle() draws a circle, right now. The Mac OS/Toolbox API, the Win32 API, the MacApp framework, the MFC framework -- every API I've ever worked with used immediate mode.
It is only by merest chance that I have any awareness of "retained mode" at all. I was on Microsoft's DirectX evangelism team back in the mid-1990's when Direct3D was first introduced. The first version of Direct3D used a retained mode API to improve efficiency (probably the same reason why Flash uses it). However, Direct3D's use of retained mode confused the heck out of game developers. As a result, they simply refused to use Direct3D, choosing to use OpenGL instead. The next version of Direct3D deprecated retained mode in favor of immediate mode, but it had lost so much mindshare in the meantime that it was a decade before Direct3D finally won over OpenGL's most vocal proponents.
Maybe all of this has changed since I stopped actively programming in 1992. Maybe now all Computer Science students are taught to think in terms of retained mode, so Adobe doesn't need to explain it anymore. Or maybe Adobe doesn't want to explain it; perhaps Adobe has too darn many ISVs already, and it wants to keep out the riff-raff by ensuring that only those who already know the secret "retained mode" handshake can get past the gate. That would be remarkably self-defeating, and although such self-defeating actions are surprisingly common, it doesn't sound like Adobe at all, but...what do I know?
Or maybe I'm barking up the wrong tree entirely, and Flash/Flex/ActionScript's drawing system doesn't used retained mode at all...in which case I'll apologize profusely.
Labels: ActionScript 3, Flash, Flex 3, programming


7 Comments:
Flash player does use a retained mode for drawing, what is happening is people are placing drawing code into the constructor (not a great habit – createChildren would be a better place IMHO) so that components graphics object is getting instructions to draw something and until graphics.clear is called on that object it will always have whatever was just draw. So in 10, 50, 40000… frames unless you call graphics.clear it’s visuals that were drawn using graphic.drawLine (or similar) will still be there.
You should check out something like http://demo.quietlyscheming.com/RandomWalk/SimpleApp.html, it’s good for understanding the visual flow as it has source code included. A decent flex component will only update its visuals on a data/property changes or resizing screen etc. So what you do is override the commitProperties() function, which gets called automatically if you call invalidateProperites() (this is because you can have several changes to an object properties but you only wish to do one update).
Anyway there is loads to say about/explain the flash/flex flow but the code says it much better so check it out (right click). And stop being so paranoid about code! There are no secrets if anything Adobe have made flex so open there is very little that you can’t find out about it.
Good comment -- thanks! :-)
Yes, UIComponent has an easily-hookable architecture, which is excellent.
Yet many examples use, and state that one should use, lighter-wieght classes such as Shape or even Sprite for drawing, but as far as I can tell these classes have no architecture at all to support drawing.
Consider TestDraw1:
app: http://www.igetitmusic.com/blog/SWFs/TestDraw/TestDraw1.swf
source: http://www.igetitmusic.com/blog/SWFs/TestDraw/srcview/index.html
There are four questions embedded in its source code, the answers to which I can't figure out.
Help appreciated! :-)
I've had a quick look at your code and I'm not sure how you've seen anything in the first place.
Anyway if you are dealing with Sprites and Flex you have to remember that you can only add a sprite to the rawChildren (i.e. you can't do myCanvas.addChild(mySprite); )
Secondly in the constructor you must specify a fill for the circle ( this.graphics.beginFill(0x000000, 1); ). The alpha is between 0 and 1 (not 100).
Thirdly you do not need to specify a height/width for a sprite (I've tried to find a link that explains the innards of the sprite but I can't find it, I'll post it later if I find it). Doing so is the reason for the odd 'magnification' you are getting & for the non show when not inside the constructor.
See the below links for your code with a few comments
http://privatepaste.com/331liv1GeO
http://privatepaste.com/4e1uElmKre
Jim,
I followed you here from a comment on Flex's LiveDocs. The problem *I* was trying to solve is the one described by kenneth above ... if you copy Adobe's examples NOTHING DRAWS (aaarrgghh! two hours wasted) because you have to add "raw" children to draw shapes. CRAPPY DOCUMENTATION ADOBE!
Anyway, getting back to the subject of your post, I'm an old-timer like you (older probably ... coding since 1980 or a tad before). I think what you are expecting is that when you draw, the drawing API is manipulating a bitmap somewhere. That would be the case, say, with Mac OS (which I haven't used since the 90s before OSX), where you were drawing into a GrafPort representing a screen bitmap. That's what you mean by "immediate mode" I think. I would call it a raster graphics model.
But the Flex model is using vector graphics. The drawing APIs just add additional vector graphic commands to a set (a hierarchical set, since there is a display hierarchy). If you've looked at vector graphics models like SVG you will have come across this. Think of the Flex drawing API as assembling an SVG image. Nothing draws until the image is rendered. The advantages are that the graphics are scalable, unlike raster graphics, can be manipulated easily and efficiently without immediate effect on the screen (to achieve this with raster graphics you'll probably remember you had to create things like off-screen buffers).
In fact, both models mix-and-match a bit -- vector models contain commands that embed raster images with raw bitmap data to support photos etc., and raster models that I've used often supported "sprites" -- small off-screen buffers repeatedly copied to the screen at vector coordinates.
Agree with you about one thing -- it would be helpful if Adobe wrote a bit of architectural documentation to accompany their crappy examples.
I should have summarised by referring to your comments about "inherited draw methods from constructors" and "self-modifying code". That's not what's going on with Flex. What is happening at actual drawing time is that the vector graphic assembled during all those API calls is now rendered. (Another advantage of that is that some of the vector commands can be dispatched straight to a graphics co-processor should the machine have one, for superior performance).
CRAPPY DOCUMENTATION ADOBE! that's very true Mr.Jim Plamondon.
Since the time(2007) I shifted my development to Adobe Flex from Microsoft's WPF I am facing this. I find similar but nothing more examples in Adobe's Tech Team blogs. Hope they understand it soon.
My problem is not with retained mode per se -- I understand the performance advantages that it brings -- but rather with the lack of encapsulation of an object's drawing code in a draw() method. Djikstra's separation of concerns, and all that. Calling drawing code in a constructor -- which is just supposed to initialize properties -- just seems nuts.
Post a Comment
Links to this post:
Create a Link
<< Home