Closed joshtynjala closed 7 years ago
Mhm, code that influences Starling's rendering like that has become a little more difficult because of the render cache. Sorry for that!
In my tests, this didn't work before the mentioned commit, either. But since that commit, the recommended way to implement such a delegate has changed, anyway, so it's good that you bring it up!
Please implement the render
method like this:
override public function render(painter:Painter):void
{
var wasCacheEnabled:Boolean = painter.cacheEnabled;
var filter:FragmentFilter = target.filter;
var mask:DisplayObject = target.mask;
painter.cacheEnabled = false;
if (mask) painter.drawMask(mask);
if (filter) filter.render(painter);
else target.render(painter);
if (mask) painter.eraseMask(mask);
painter.cacheEnabled = wasCacheEnabled;
painter.excludeFromCache(this);
}
Some notes:
filter
and mask
need to be checked manually is that those are always the parent's responsibility. If you look at DisplayObjectContainer.render
, you'll see that it renders filters and masks for each child. (This could have been avoided, but only at the cost of additional method calls ... which would have hurt performance. Thankfully, this is an issue only very rarely: within Starling, only RenderTexture
replicates this code.)cacheEnabled
property has two effects. When the cache is disabled, (1) no render calls are recorded and (2) nothing is drawn from cache. Your previous setRequiresRedraw
would have worked, too, but this is cleaner, because you don't change the target object in any way. It will still be drawn from cache in the next frame: only the RenderDelegate
will need to redraw it.painter.excludeFromCache(this)
, you're making sure that this render method is called in the next frame. By using this instead of setRequiresRedraw
, you avoid that skipUnchangedFrames
is disrupted.Please try it out — that should do the trick!
I actually oversimplified my example, I think. This should be what worked correctly before that commit and failed after:
var sprite:Sprite = new Sprite();
var quad:Quad = new Quad(100, 100, 0xff0000);
quad.mask = new Quad(50, 50);
sprite.addChild(quad);
sprite.x = 40;
sprite.y = 40;
this.addChild(sprite);
var delegate:RenderDelegate = new RenderDelegate(sprite);
delegate.x = 40;
delegate.y = 150;
this.addChild(delegate);
Your changes seem to fix the issue with this new code too! I spent at least an hour on Friday trying various combinations of cacheEnabled and excludeFromCache(). I don't know why I didn't get it to work, but I guess I was calling things in the wrong order, or maybe I passed the wrong display object to excludeFromCache(). Regardless, it works now! Thanks, Daniel.
I actually oversimplified my example, I think. This should be what worked correctly before that commit and failed after [...]
Yes, that's what I thought! I used exactly that code for my first test. :smile:
I spent at least an hour on Friday trying various combinations of cacheEnabled and excludeFromCache(). I don't know why I didn't get it to work, but I guess I was calling things in the wrong order, or maybe I passed the wrong display object to excludeFromCache().
Yeah, I can imagine ... only the order of cacheEnabled
is relevant, excludeFromCache
can be called anywhere in the method — but you have to pass the right objects, and I can totally see that this is tricky. If it wasn't so damn useful, I would have thrown that cache away already, I assure you! :wink:
Feathers has a class called RenderDelegate with a render() overide that simply calls render() on another display object. It basically allows the target display object to be rendered with different transformations. I use it for animated transitions so that, once the animation is completed, I don't need to worry about resetting any changes that I made. Here is a simplified version of the RenderDelegate class:
After commit 70f98e86bf73dc1ff6b690ddfb7ab48d52c8d11b, if the target (or one of its children) has a mask, that mask is not included when the RenderDelegate renders. See the following code to reproduce: