Graphical artifacts when animating scrollRect property in ENTER_FRAME handler?
Copy link to clipboard
Copied
I tried to animate a drop down menu by assigning a rectangle to the scrollRect property of the menu.
The rectangle's width is fixed, and the height of the rectangle is animated from zero to the final height of the menu over a 250ms interval.
The alterations to the scrollRect originate from a master handler for ENTER_FRAME, which runs a series of registered task functions, one of which is my menu animation function.
As the menu drops down, various areas of the child DisplayObjects, including the background, fail to draw completely. If I resize the stage at all, then everything is redrawn properly.
Are there any known problems with animating the scrollRect property when:
- altering a scrollRect from within an ENTER_FRAME handler
- when child objects also have a scrollRect set?
The exact regions that fail to draw are inconsistent each time the menu drops down, but they occur in the standalone player as well as in web browsers such as Firefox and Internet Explorer.
See video here: glitches2.mp4 - Google Drive
It clearly doing something wrong, as evidenced by the redraw regions at the end of the animation (see image). At the end of the animation, the scrollRect is set to null. The bottom of the menu is still not updated, and neither is the drop shadow filter on the menu.
Update: The artifacts go away if I set cacheAsBitmap to true: glitchesRemoved.mp4 - Google Drive
Interestingly, they also go away if I remove the dropShadow filter from the background child control and leave cacheAsBitmap off.
Anyway, this problem seems to manifest itself specifically when animating the scrollRect property of an object with cacheAsBitmap set to false that contains a child control with a filter applied.
Here is a final video with everything working. The drop shadow filter is on the object itself, rather than the background, which sets cacheAsBitmap to true as a side effect:
Copy link to clipboard
Copied
Just a quick question. Are you invalidating the component every time you alter the innards so it knows it needs to redraw itself? If you alter something a component handles itself differently then it won't automatically know to invalidate itself and cause a redraw.
Copy link to clipboard
Copied
It might be more performant if you just grab a BitmapData of the menu and then draw that in, replacing it with the real menu once it's in place. The code would work similar to this Amy's Flex Diary: Endless Scrolling Bathroom
Copy link to clipboard
Copied
Performance isn't my concern here; I have a very complex interface running at 60fps with no lag during layout or anything (the video below is not that interface, but just a test of the menu effects).
The problem I'm concerned with is simply that Flash is rendering the display incorrectly when animating the scrollRect. The scrollRect's size is increased by each frame, but Flash isn't drawing the contents entirely for no apparent reason, unless cacheAsBitmap is turned on.
And sinious, don't let the style of the drop-down fool you; there is no invalidation going on here, because I'm not using Flash components or Flex. This is using a more advanced framework I developed (dozens of button states, dozens of docking modes, auto-sizing to contents, non-framework control wrapping, override of width/height to use internal measurements, a completely overhauled focus loop, etc.). Here is the same menu with some other effects turned on, including Aero glass-like effects that blur the background. Any lag is simply the video capture not occurring quickly enough.
Copy link to clipboard
Copied
So you're just using the animation system normally on common display objects.
My only other question would be, because you described it as sophisticated, are you using Stage3D? I could think of a few vertex buffer reasons why artifacts like this could happen but if you're just using the standard Flash display list then this is definitely the first time I've seen this behavior. And it's not without you doing your duty as any good developer, toggling everything in sight on and off, isolating the issue as much as possible.
Since you use no components and have coded your own list/dropdown/scrollbar and you're animating a mask that reveals the drop-down, have you animated a mask over any other simple display object to see if the same effect happens? Essentially cacheAsBitmap is doing just that, flattening the object into something simple to render (a single bitmap) under the hood until you need it to change, so what Amy said is pretty useful for this. You don't need the list to move while you animate it in anyhow. But how far down the ENTER_FRAME animated mask bunny hole does the glitch go? Can you reproduce this in a FLA you can share with the bug reporting team?
Copy link to clipboard
Copied
I'm just animating the scrollRect property to achieve a simple clipping effect that reveals the menu. Once per frame, it updates the scrollRect property, assigning a new rectangle with a height of "p * height", where p is the percentage through the animation (0 to 1). There is nothing, otherwise, happening to the DisplayObject during the animation of that property. The DisplayObject's height is not changing, nor are any other properties changing (besides scrollRect), so it's essentially static while it's being revealed. When the scrollRect's size is increased frame-by-frame, Flash Player is simply failing to render the entire clipped area, almost as though there is a frame lag such that it's rendering the clipped area from the last frame, rather than the new area exposed by the latest value of scrollRect. When I simply turn on cacheAsBitmap, it renders correctly and completely. The only "interesting" thing about the menu would be the DropShadowFilter applied to a child, so my best guess is that there's some sort of bug in Flash Player's rendering system involving scrollRects whose size changes when one or more child objects have filters applied (just a guess from what I'm seeing). To fix it, I moved the DropShadowFilter off the child object and onto the menu itself, which kills two birds by forcing cacheAsBitmap on (removes the artifacts) and also ensures the drop shadow is applied to the clipped area rather than being clipped by the scrollRect. I'd imagine I could replicate this easily in a simple FLA that I could post, so I 'll try that.
As far as the "sophisticated" nature of the framework, I was primarily referring to its layout engine, in the sense that it's non-trivial, finely-tuned, and powerful compared to existing offerings. There are no Stage3D effects; I ruled out using that years ago when I was seeing graphical artifacts throughout the display list whenever any 3D effects were applied (even setting DisplayObject.z to zero), because it seemed to change something about the overall rendering mode. For example, I took these screenshots in 2010 and posted them to the bugbase:
What started off as a slight offset that appeared when 3D rendering was active, turned into very obvious glitches at the edges of objects, as revealed in the latter two images; the random speckles of color and the yellow lines at the bottom and right edges of the button are not supposed to be there. (The striped lines in the overall background are supposed to be there; they are part of a "circle splash" shader filter that collapsed in from the edges of the screen making it look like the background is being eaten by the event horizon of a black hole, haha.)
Anyway, the MenuTest.mp4 video I posted is actually using off-screen rendering to BitmapData via DisplayObject.draw, along with the application of a blur and tinting for the glass effect. The real-time ripple effect is rendered with an animated ShaderFilter I wrote with PixelBender.
Copy link to clipboard
Copied
Here you go, reproduced the problem from scratch, as promised, in a simple FLA. This should remove all doubt.
scrollRectGlitch.fla - Google Drive
Go ahead and click both buttons. Click back and forth between them, click one a couple times, and click the other a couple times, and just observe the differences in how they are rendered.
The first time you click "animate with cacheAsBitmap = false", it actually renders correctly, but click "animate with cacheAsBitmap = true", then go back and click "animate with cacheAsBitmap = false" again. Now, you will see that in addition to failing to render completely, it often fails to render the intermediate steps at all, and always fails to render the final frame, leaving the menu in a partially clipped states.
Identical logic runs for both animations; the only difference is that cacheAsBitmap is set to true for one, and false on the other.
There is obviously something fishy going on. The problem seems to occur when there is a filter on a child object. If the drop shadow is removed completely, the problem goes away, but the fact that it's occurring at all and behaving so erratically tells me something is wrong. Something is not being updated properly when scrollRect is changed.
Perhaps this note in the documentation is a clue: "Note that changes to the scrollRect
property are only processed when the object is rendered. Thus methods like localToGlobal
may not produce the expected result if called immediately after modifying scrollRect
." That might explain why it renders correctly when cacheAsBitmap is true, but there are confounding factors since cacheAsBitmap causes scrollRect to render differently using optimized routines. Also, ti doesn't explain why it renders properly when no filter is present on the children. This is a real problem and a deterrent from using the technology. Furthermore, even taking the note into account, I would need a way to force an object to re-render, even though nothing is changing on the object. Apparently, I'm not the only one to notice this: cacheAsBitmap and Masks don't get along. at Design Software, so this may a problem with bitmap cache updating.
Copy link to clipboard
Copied
Probably the filters can't be processed in addition to your ENTER_FRAME logic in time for everything to render at the frame rate you want.
Copy link to clipboard
Copied
Well that can't possibly be the case for two reasons. First, I just posted that simple example demonstrating that it's perfectly capable of rending a single DisplayObject with a DropShadowFilter at the target framerate: scrollRectGlitch.fla - Google Drive It's just not redrawing the object when the scrollRect changes, and I think that note in the documentation has something to do with it.
Secondly, I've narrowed the problem down further, and it's not the filter that's the problem, but the fact that filters force cacheAsBitmap to be true. The Player seems to have problems rendering any child object that has cacheAsBitmap set to true (which happens to include those with filters), when a scrollRect applied to the parent with cacheAsBitmap set to false. For example, if I remove the drop shadow filter on menu.list (effectively turning off cacheAsBitmap on menu.list), and instead set cacheAsBitmap = true on menu.list.slider, then you'll see that it renders menu.list (the red menu items) properly but exclusively fails to draw the scroll slider, which now has cacheAsBitmap set to true.
So when you update a scrollRect, the Flash Player seems to want to draw everything EXCEPT children (or even grandchildren) with cacheAsBitmap set to true. I think Adobe needs to look into the non-optimized code-path for scrollRect (the one that runs when cacheAsBitmap is false), to make sure it is properly updating children with cacheAsBitmap set to true.
This same problem appears to have been encountered as far back as 2011: caching - ActionScript - Problem moving scrollRect with cacheAsBitmap - Stack Overflow
Copy link to clipboard
Copied
Ah, well I just follow the directions:
DisplayObject - Adobe ActionScript® 3 (AS3 ) API Reference
You can scroll an object up and down by setting the y property of the scrollRect Rectangle object.
I just hooked a button instance named debug_btn to:
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
stage.align = flash.display.StageAlign.TOP_LEFT;
debug_btn.addEventListener(MouseEvent.CLICK, onDebug);
var rec:Rectangle = new Rectangle(0,132,125,132);
menu.scrollRect = rec;
function onDebug(e:MouseEvent):void
{
// reset to y position +132
rec.y = 132;
stage.addEventListener(Event.ENTER_FRAME, onEF);
}
function onEF(e:Event):void
{
// just running at 1px=1fps
if (rec.y-- > 0)
menu.scrollRect = rec;
else
stage.removeEventListener(Event.ENTER_FRAME, onEF);
}
No, it's not a "reveal", it's slowly descending, but the x/y properties work as expected.
I didn't enable cacheAsBitmap (although again, I don't know why that would be a bad thing since you're not breaking any rules and it will increase performance).
My results:
http://www.ertp.com/tmp/scrollrect.mp4
Think about trying to set the height every single frame like resizing your 'camera' resolution each frame. Much easier to pan a mask anyhow. The older AS2 docs even mention not to really try this (vector redraw overload) without cacheAsBitmap.
That said, I cannot animate the rec.height property and get the same results. Nothing appears, unless I set cacheAsBitmap=true.
I also set the numbers just larger than the clips dimensions which weren't taking the shadows into calculation (nor did I starting at 0,0), but just so it's clear why I used random numbers.
I'd take Amy's suggestion to make a simple bitmap representation of your object and reveal that rather than the menu itself. Flash makes it very easy to do this and no matter how complex your menu is the performance will be as optimal as possible, unless you step back into Stage3D. I think you should give 2d context with Starling another look.
Copy link to clipboard
Copied
Yeah, I know how scrollRect works, lol. I'm using it that way in my scrollPanel, such as when the menu in my original videos scrolls. In any case, there are numerous alternative ways of animating the menu.
The problem is that when scrollRect is applied without cacheAsBitmap, particularly when scrollRect increases in size for any reason, the player is failing to properly redraw, exclusively, any descendent DisplayObjects which happen to have cacheAsBitmap turned on. I can't stress the fact enough that this is NOT a performance issue; it occurs in the most simplistic cases where neither complex enter-frame logic nor vector overload could possibly be an issue. It's strictly the situation where when scrollRect is on and cacheAsBitmap is off, then children having cacheAsBitmap = true are not updated properly. That's about as accurate a description of the problem as I can possibly provide.
The best advice I could give anyone else encountering this problem is... if you're using scrollRect, make sure cacheAsBitmap is turned on as well. Without cacheAsBitmap, it's not only slower, it renders incorrectly.
Copy link to clipboard
Copied
We're all on the same page. There's an issue with adjusting the dimensions of the scrollRect, and cacheAsBitmap may or may not play a role.
As I mentioned in my example I couldn't just leave my rec var at Rectangle(0,0,menu.width,0) and animate the rec.height property each frame. Nothing shows, unless I set cacheAsBitmap=true on 'menu'.
For any copyright purposes you might want to remove your graphics from the FLA and simply provide a basic shape or anything for-position-only and submit it into the http://bugbase.adobe.com with the cacheAsBitmap workaround.
I was testing on FP and AIR 13 (latest at date of reply) and I'll vote it up if you post the link.
Copy link to clipboard
Copied
I opened a bug: Bug#3766888 - Children and decendents with cacheAsBitmap ON fail to draw when resizing parent's scro..., and I updated the example FLA to include 3 experiements, just click the purple button to cycle through the experiments, and the two blue buttons to test each experiment.
Updated FLA with 3 experiments demonstrating the rendering/updating failure:
scrollRectGlitchExperiment.fla - Google Drive
The effect is very obvious in Experiment 3, where I turn on cacheAsBitmap for specific menu items and turn then vertical, and those specific items fail to render exclusively while the ones beside them with cacheAsBitmap off render fine. Can't be any more obvious that that.
Copy link to clipboard
Copied
Got my vote, good find and good luck!

