openGLRenderer & 2D drawing


#1

I’ve been looking at how the OpenGLRenderer stuff works and had a couple questions regarding 2D drawing.

Does it make any sense to draw into a low level OGL context directly vs. just using the regular paint methods? if you’ve attached your context to your component, paint() is going to be called on the OGL thread. So, what is to be gained from doing this:

void SomeComponent::renderOpenGL()
{
    //draw directly into the low-level glRenderer context.
    const float desktopScale = openGLContext.getRenderingScale(); //takes retina scaling into account
    ScopedPointer<LowLevelGraphicsContext> glRenderer (createOpenGLGraphicsContext (openGLContext,
                                                                                    roundToInt (desktopScale * getWidth()),
                                                                                    roundToInt (desktopScale * getHeight())));

    if (glRenderer != nullptr)
    {
        Graphics g(*glRenderer);
        g.drawImage(//...)

vs.

void SomeComponent::paint(Graphics& g)
{
    g.drawImage(//...)

?

Unless we need to use specific GL stuff, based on the juce demo code for OGL 2D drawing, it doesn’t seem like any performance is gained if you draw directly into the context vs. drawing in paint(), seeing as paint() will be called on the OGL thread anyway, instead of the message thread. The only thing I can see is that a couple extra lines of code are called setting up the context, and you have the headache of making sure you accounted for retina scaling (thanks for showing us how to correctly handle that in the demo code btw).


#2

The benefit comes from having a unified interface for drawing a Component - so if you end up running into bugs in your draw code from OpenGL (which is likely unfortunately) you can just fall back to the CoreGraphics or software backends without having to do any extra work.


#3

Now that I’ve figured out how to draw using it, i’m not sure I’ll ever go back! no need to use a timer to trigger repaints, and the CPU usage dropped by 75% for drawing a rectangle on the screen that tracks the mouse when using OpenGL!

I guess the smart thing to do is have an option that enables/disables OpenGL rendering in the app?


#4

Whoa! Were you using direct GL methods, or using an in-callback Graphics context like in your first post?


#5

in-callback graphics context just like in the Juce Demo for OpenGL "draw 2D background"


#6

I’m trying to figure out where the lagging behind the mouse cursor is coming from. any ideas where to start? it’s the same lagging for MessageManager-based repainting and OpenGL repainting.


#7

here’s with a Release Build. that difference using OpenGL painting in CPU usage is :ok_hand::ok_hand::ok_hand::ok_hand::ok_hand::ok_hand::ok_hand::ok_hand:


#8

I would ping some folks on the dev team @jules (?) and ask. Really interesting, I’ve always had terrible performance with the GL backend (render was fine as you’re describing), and when the performance wasn’t bad I was always getting rendering bugs on macOS.

As for the mouse movement, are you storing position updates from the mouseMoved callback or fetching them directly in the GL callback? That could make a difference in responsiveness (but probably not a huge one).


#9

i’m capturing mouse X/Y in the mouseListener overrides. I’ll investigate getting the mouse coordinates right from the graphics context. I didn’t know you could do that.


#10

so, capturing the mouse position via

Desktop::getInstance().getMouseSource(0)->getComponentUnderMouse()->getMouseXYRelative();

made no difference in the lag between the mouse cursor and the rectangle being drawn on screen. any ideas on that @jonathonracz @jules @t0m


#11

I considered trying to make a translucent OpenGL window that sits on top of my app window, but every thread I found about it basically says “bake your window into a background image and draw that as your OGL background”, which doesn’t solve the problem of the lag between the cursor and the rectangle being drawn.

For what it’s worth, if I stick with plain old MessageManager-based paint() rendering without a timer, and call repaint() in each mouseListener method being overridden, there is no lag between the cursor and the rectangle.

i.e.

void mouseMove(const MouseEvent& event)
{
    mouseRect.setXY( event.getPosition() );
    repaint();
}

It seems that the issue is the timing difference between when OGL repaints continually (or when a 60hz timer fires) and when the Operating System sends out MouseListener events. It’s almost like the OGL thread repaints are delayed by 50-100ms, compared to when mouseListener events arrive… I’m not sure though.

I would love to know the proper answer though. A way to combine 60hz background animation with however fast the OS repaints when it’s not on a timer. Any ideas @jonathonracz @jules @t0m @fabian or any other pros? I know @yairadix talked about it in this thread: Transparent OpenGL Component but that uses the same ‘bake it to a background image’ approach. his SurferEQ2 plugin is cool though. I haven’t checked if there are lags between the cursor and GUI response if he’s doing that image baking trick though…

it appears that his plugin does not have gui lag, based on the youtube video for it.


#12

alright, this is the best performance I’m able to get out of this. it’s still laggy, so I am not sure what to do to improve it. but I noticed that the JUCE demo for animations is also laggy, so maybe it’s just a JUCE thing, and the only real solution is to code it natively.


#13

My (somewhat) educated guess is that there’s no lag when doing a MessageManager based repaint because the message thread (where mouse events are coming from) and the GL thread (where rendering is happening) are forced to operate in lock-step - the system can’t accept more mouse events until it’s rendered the current one.

When you specifically use the explicit GL rendering callback you’re submitting commands to the GPU directly, bypassing whatever queueing/synchronization the MessageManager lock is doing - meaning there’s likely to be some delay between when commands are queued and submitted to the GPU and when things actually appear onscreen.

I’m guessing the massive CPU overhead of using the GL backend is caused by having to lock the MessageManager followed by synchronously executing all the GL commands.


#14

ah, that makes sense. but if you look in the video, there’s even more lag when using a MessageManager timer to trigger the repaints compared to OGL. So, it seems like the best thing to do is just call repaint() directly from a mouse event and not use a timer at all. but if I do that, then I can’t have an animation happening continuously in the background… :-/