Severe performance drop with JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1 when using g.drawImageTransformed on large images

Hello,

I’m encountering a major performance issue when rendering large images using g.drawImageTransformed(...) in JUCE on macOS with JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1.

When this flag is enabled, my frame rate drops significantly (well below 10-30 FPS, + GPU/CPU 90%-100%) during image drawing. But when I disable the flag, everything runs perfectly smoothly at over 120 FPS, without any lags.

Here’s a simplified version of the rendering code I’m using:

const AffineTransform transform = AffineTransform()
    .translated(-w / 2.0f, -h / 2.0f)
    .scaled(drawBounds_.getWidth() / w, drawBounds_.getHeight() / h)
    .rotated(angle)
    .translated(centre.x, centre.y);

g.drawImageTransformed(image, transform, false);

The image being drawn is quite large (e.g., full-screen or bigger), and performance degradation seems to grow with image size.

Has anyone else faced this issue? Is there a recommended way to optimize drawImageTransformed usage with this flag enabled, or is this a known limitation of the CoreGraphics multi-paint implementation?

Also, ChatGPT mentioned that when using CoreGraphics (with JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1 ), especially on macOS where Metal is involved under the hood, the system may invalidate or re-upload the image texture to the GPU each time a transform is applied.

p.s. this happens for me on iOS, iPadOS and on any mac os version.

Any insight or suggestions would be much appreciated.

Thanks!

2 Likes

JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS=1 fixes an issue where multiple dirty components tended to cause the entire area between them to be invalidated. Basically, if you are repainting an item in the top left, and another in the bottom right, for example, the entire window would repaint.

I thiiiiiink, that the Images issue you reference happens when you are using SoftwareImageType type as the image type (instead of NativeImageType).

So, when creating the image in the components constructor or wherever, you need to do something like:

 Image myImage {Image(Image::ARGB, 500, 200, true, juce::NativeImageType};

And also make sure you aren’t setting JUCE_USE_SOFTWARE_IMAGE to 1.

Thank you, I tried to set juce::NativeImageType, but same issue.
Unfortunately I’m already rewriting it for Skia graphics library. It turned out that everything is super fast and at 120 fps, no matter what I draw, and there are shaders. I don’t know why Juce doesn’t want to use it. I drew thousands of lines and thousands of pictures on the screen and it’s still 120 fps and CPU is low, even on older iPad/mac devices.