AnimatedAppComponent + setOpaque (false) = poison

I ran into a funny property of AnimatedAppComponent after implementing pretty usual fade in/out overlay animations. That kind of stuff is fairly simple to implement using AnimatedAppComponent, but…

The class is actually quite insane when setOpaque (false). First of all, the component’s timer never stops, but keeps calling repaint () no matter whether the component is visible or not. Now if the component is not fully opaque, the repaint causes every underlying component to repaint itself. There seems to be no way of stopping the updates (except by disposing of the component), as the Timer is a private base class.

Having a half a dozen “full screen” overlays idling invisible, pretty much destroys the UI performance. This effect is quite sneaky and appears gradually as you keep adding such components. I recommend you never use this class. I would suggest you use something like this instead.




#pragma once

#include <JuceHeader.h>

class Animated : public    juce::Component,
                 protected juce::Timer
	virtual void update() = 0;

	Animated(int frequency) : frequency (frequency)

	void start()
		lastUpdateTime = juce::Time::getCurrentTime();
		startTimerHz (frequency);
	void stop()
		stopTimer ();
		repaint ();
	int getDeltaTime () const
		auto deltaTime = juce::Time::getCurrentTime() - lastUpdateTime;
		return int (deltaTime.inMilliseconds());

	void timerCallback () override
		lastUpdateTime = juce::Time::getCurrentTime();

	const int frequency;		
	juce::Time lastUpdateTime;


Call start/stop according to your present/dismiss logic and you should be able to have as many of these as you like, opaque or not.

I wouldn’t recommend this class either, because it is trivial to write your own. However:

There is good reason to have this class opaque by default. You should avoid having it (semi-)transparent, because in this case a repaint() will cause the parent component to repaint as well, which can easily cascade up to repainting the whole GUI at each timer call.

Yes, that’s what happened in my case. The entire UI was updating every frame or something. The performance hit is quite harsh. Of course if you know JUCE inside out, you won’t do these kinds of mistakes, but on the other hand, then you probably don’t need AnimatedAppComponent either.

In my experience alpha is the single most common animated property in a UI, so IMHO having a (general purpose) class that can only animate anything else is kinda useless (unless it’s used to optimize special cases that are fully opaque).

Anyway the good news is that my UI runs a lot smoother now. Perhaps someone may find this post useful.

Yeah pretty much that, AnimatedAppComponent is just a few lines wrapper around Timer, I tend to implement this behaviour myself too usually, activating/deactivating it only when needed.
Just wanted to add some related things to look out for, since I recently went through a pretty heavy software rendering rollercoaster trying to get a fancy visualizer to render fast enough. I ended up implementing my own antialiased line plotter and used all kinds of 1990s demoscene approaches to get this fast enough, since it turned out that using any of JUCEs path drawing or blending things killed performance too quickly - and what I rendered wasn’t even close to fullscreen :wink:
this is about software rendering - if you are okay with using opengl, maybe check out NanoVG as a lightweight solution for rendering a few things.

some learnings for CPU rendering:

shared ui performance in plugins:
if we’re talking plugins, ui performance is usually shared with other plugin instance in one single thread. a good test is using Reaper and testing how many instances of your plugin ui you can open at the same time until Reapers UI freezes to a standstill. prepare to raise your eyebrows, it might be worse than you expect. (bonus challenge: enable 2x high dpi)

transparency bad:
pretty much what has been written here already. if something is not opaque, the underlying components must be rendered. buffering to images may help, but in case of software rendering it mostly comes down to fillrate/memory access.

repaint debugging and profiling:
enabling repaint debugging, having the invalidated components flash in random colors, helps getting an idea what is being rendered. try to layout your components in a way that they do not overlap, even if one pixel of an underlying component overlaps with a transparent component on top of it, it’s paint method will be called. clipping reduces some pixel access, but doesn’t help much more.
in addition to that, it’s a good idea to add some highresolutionticks time measurements to various paint calls, or just do it in Component and log everything.

paint calls unnecessarily exceeding screen FPS a lot:
this may be mitigated in juce7, haven’t checked yet how well it works - but as far as juce6 goes, on windows there was no limit on the repaints - as soon as something invalidated, that led to a paint, as fast as the windows message dispatcher can go, potentially executing hundreds of repaints per second.
this can be caused by automated parameters attached to sliders, for example - but mouse events can cause it too, e.g. when dragging a slider. so if you do a performance test in Reaper with a few open windows, add some automation and use a small audio buffer size to cause tons of ui invalidations and take this into account for rendering performance.

in addition to what was already mentioned, it’s a viable option to write own renderers for particularly heavy things in your ui, avoiding the overhead of JUCEs drawing methods. from my experience, directly pushing rgba data into an image on a low level basis does work if you are careful with pixel formats etc. think of it like writing shader code on the CPU and try to touch each memory address only once, using small acceleration structures, and avoiding all the method call overhead that typically occurs when using the juce graphics API (just step into the deceptively innocent looking drawLine).

make sure to take actual rendering dpi into account, because any resampling should be avoided at all costs when not using hardware acceleration. but blitting without a transform is as fast as it can get.
with the same approach, I “baked” background graphics that rarely change into an image. you can do this with buffering components to images, but in some cases it may be worth doing this yourself, with the opportunity of creating a shared cache and reusing previously created versions at the cost of a few megabytes of ram.

just my two cents, maybe it gives you some further ideas optimizing your rendering.


1 Like

I’m pretty sure this class just exists as a simple demo / setup which is akin to something like processing or open frameworks where this is the standard operating procedure – not something to be used in an actual app