OpenGL with setBufferedToImage is very slow (+ bug-fix)

When using OpenGL, setting setBufferedToImage on child components makes things super slow (in my example some rather static components taking 20% cpu when not cached take 60% when cached!)

This is due to the image's cached texture being invalidated even when the component doesn't get redrawn, due to a bug. (A Graphics context gets created for the image even when the component isn't invalidated, and this invalidates the cached texture)

Here's the fix: https://github.com/yairchu/JUCE/commit/11c0998feba41c2ea674d739735124a7b1b06c3a

In my above example the 60% cpu turns into 10% after the fix, making a nice improvement over the 20% with just a few setBufferedToImage calls.

Also, before the fix most of the CPU was spent in juce::Flipper, flipping the images before loading into an OpenGL context. I suppose that could be sped up significantly by maybe having the textures being stored flipped??

Cheers! Yair

Thanks, I'll take a look!

Re: flipped textures, it was a long time ago when I wrote that so I forget the details, but I'm sure there was a good reason why I couldn't just draw them the other way up..

Is there a reason why you removed the check for lg.isClipEmpty() ? I can't really see the point, since if the clip is empty, none of that code would do anything, right?

I think that the purpose of "lg.isClipEmpty()" was to check whether any invalid area remained.

That condition will always be true given the new condition of "!validArea.containsRectangle (compBounds)" which means that not all of the component is valid (therefore some invalid area will remain).

I'm struggling to satisfy myself that this will work correctly in all circumstances.. I've a feeling that it could lead to things not getting painted, but haven't time to really focus in and analyse exactly how that might happen.

I'll try to explain the change:

Previously, a Graphics context drawing to the image was always created, invalidating the image's cached texture.

BUT, the image was then only drawn to "if (! lg.isClipEmpty())". That is, if the after excluding "validArea" from the context's clip there still remains an area to draw into.

I wanted to avoid invalidating the cached texture when we are not going to then use it to draw to the image - that is, when "lg.isClipEmpty()".

However, this condition is using the created Graphics context, which by its existence already invalidated the texture, which is what we wish to avoid, so I had to formulate the condition in a different way: "if (!validArea.containsRectangle (compBounds))".

This new condition is equivalent to the old one but does not create a Graphics context, so it avoids invalidating the texture.

Why is it equivalent? If the valid area contains the whole bounds of the component, it means that the component is valid, and does not need to be redrawn at all. It is just the same as creating a graphics context for the component, clipping out the valid area, and checking if the clip is empty.

Ok, gotcha!

1 Like