Graphics::drawImage 100x slower on retina since Logic 10.5, than on non-retina

When i move the plugin-window from the non-retina display to the built-in retina screen of my MacBook-Pro 13inch, the draw image performance increases dramatically (by factor 100, from 2ms to 200ms), any hints? (Logic 10.5 , Deployment Target 10.9, Base SDK 10.15)
(The image has a non retina solution, so must be internally scaled up by two), I’m pretty sure this is a new behaviour since logic 10.5, i will do further tests.

Does anybody has ideas what can be the reason, or how it can be improved?

EDIT: Its also possible that this effect was introduced through a recent macOS update or a change inside juce, i will check this

UPDATE: okay, i think the latest macOS update must be the culprit

here is the culprit:

For my retina-optimisation i use the physicalScalFactor and it returns 4 instead of 2, which i guess is a bug, so the internal buffer was created was much higher resolution than required.

Are you using Components with CachedComponentImages? I think those are also affected… or at least have been in the past, so I stopped using them at all on OSX.

I just checked on Juce 5 develop and it indeed is still broken after multiple years. sigh. This bug also slows down some path and font rendering as the curve resolution is determined by the same getPhysicalPixelScaleFactor.

I reported this here and the conclusion was that the problem doesn’t happen in all hosts. In my case it happened in AULab and still does:

In the meantime I checked with Logic 10.4 and there I get a scale factor of 2.

So maybe the issue does come from Logic 10.5.

i got this issue independently also in a standalone plugin wrapper. Still not clear what introduced the issue, but i think it was through an macOS update.
I use a background thread for creating the image.

Wow… ok for me a standalone build returns 2.0. But I have only one display which is of course a retina display.

float CoreGraphicsContext::getPhysicalPixelScaleFactor()
{
    auto t = CGContextGetCTM (context);
    return targetScale * (float) (juce_hypot (t.a, t.c) + juce_hypot (t.b, t.d)) / 2.0f;
}

In the case of AULab, the CoreGraphicsContext gets created with scale=2.0 which leads to targetScale being 2.0. t.a is 2.0 and t.d is 2.0 which leads to another 2.0 factor and to the resulting 4.0.

scale comes from NSViewComponentPeer and is detected using

if ([screen respondsToSelector: @selector (backingScaleFactor)])
    displayScale = ( float ) screen.backingScaleFactor;

This seems all correct so far.
The transform t comes from the CGContext that is used by NSViewComponentPeer.

I guess the issue is that the CGContext already has scaling applied to it in AULab (and maybe now in Logic 10.5). I guess to avoid the issue NSViewComponentPeer would need to check the scaling of the supplied context and maybe reset it… or just make sure not to reapply it when the factor is calculated … not sure why it is necessary at all… IMHO the backingScaleFactor alone should always be the correct number for JUCE to use?

I just checked in Logic 10.4 with the debugger attached and there I do get a CGContext with a t.a == t.d. == 1.0 transform.

Any comments about this from JUCE ?

I see that there is a serious plugin graphic performance issue on the latest Logix Pro X, not just my plugin but others as well. When I open two plugin instances I can hardly move the Logic faders. No such issue on other DAW’s as I can observe. Something is definitely weird, and not at all useable like this. What has Apple done this time ? Any impromevents are expected soon ?

Removing the transform scale factor from the calculation results in incorrect rendering in some cases. There was a discussion about this a while ago:

It seems like Logic is the issue here as it is scaling the CGContext in addition to the backing scale of the screen. Is this only happening in Logic 10.5?

Is this only happening in Logic 10.5?

No (i thought first), happens also with the Standalone Wrapper, but i will checkout whats happening later, it maybe dependents also on hardware.

For me it also happens in AULab and maybe other Apple hosts are affected. Note that AULab is the reference host of the CoreAudio team, so the same way of hosting might be used by people using Apple sample code. It’s all just a guess and I did check MainStage which is not affected. IMHO the issue is that JUCE expects an unscaled CGContext. Or maybe getPhysicalPixelScaleFactor() is misnamed and misused, as it seems very unlogical it would return anything else than screen.backingScaleFactor - well actually it probably needs to factor in scaling done by transforms inside the JUCE drawing code.

The pull request you linked “solved” the issue by always using 1.0 for backingScaleFactor, but that’s the wrong fix - backingScaleFactor should always be used, but the transform factor should be determined more cleverly and not use any factors that were already applied when the CGContext was passed into JUCE.

Only for the matter of optimisation it would be great to have two functions.

  • get the exact physical size of a component in pixels (inclusive applied transforms) - rounded to ints

  • a physical drawImage method which does not do any resampling.

(I know that pixel based drawing is the past, but this is for the matter of background thread optimised painting)

1 Like

I can confirm that this happens on the Standalone version and on Logic Pro X ( for sure on the latest version ) There certainly has been a change on the latest versions of Xcode / OSX which has destroyed the graphic efficiency and rendering process of JUCE . I think this is an urgent issue, since the UI response is getting below any standards, quite unacceptable.
I am testing on other DAW’s (Reaper, Live, Bitwig) and no such negative UI response impact is happening on them.

OK, this should now be fixed on the develop and juce6 branches. We can use the CGContextGetUserSpaceToDeviceSpaceTransform method to get the correct transform for the context in all cases and no longer need to query the backing scale factor of the display in addition to the transform applied to the context. This also solves the issue in the previous PR that I posted of low-res rendering when using cached components or component effects.

4 Likes

Thanks Ed! This has fixed my long-standing issue with AULab and possibly some other CoreAudio based hosts as well. It also affects macOS versions prior to 10.14, I’m currently developing on 10.13, but I’ve seen the issue on much older versions… possibly all of them supporting retina displays.

Extra thanks for adding this to the juce5 develop branch.

Interesting, I was only seeing the upscaling in standalone apps built against the 10.14 base SDK or later but it’s possible that AU Lab and Logic were already scaling the CoreGraphics context for AUs on retina displays.