Compression for Mouse move events


#1

I’m having this problem on Windows, i’m assuming i’m going to get the same trouble for other platforms.

I want to perform repaints during a mouse drag operation. The components being repainted are those behind an object being dragged. If you do this is the normal way, it works but you get quite a lag in painting. the appropriate calls to `repaint’ do not repaint synchronously (nor should they), but you dont get much control over exactly when things do get painted.

so, like a hacker, i do this during my `mouseDrag’

        ComponentPeer* p = getPeer();
        if (p)
            p->performAnyPendingRepaintsNow();[/code]

that works too, except that now the painting goes ahead whilst Windows is happily buffering up more mouse move events. What happens is, after i stop moving, the drag goes on and on playing out buffered move events in a comedy way.

So now im looking to "compress" mouse move events in the system queue. By compress i mean; "replace all pending moves by the last one".

I've done this on the Windows build in `juce_win32_Messaging.cpp dispatchNextMessageOnSystemQueue like this:

[code]bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages)
{
    using namespace WindowsMessageHelpers;
    MSG m;

    if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, 0))
        return false;

    if (GetMessage (&m, (HWND) 0, 0, 0) >= 0)
    {
        // if we have a mouse move, eat up any pending on the queue
        // so as to deliver the last one.
        if (m.message == WM_MOUSEMOVE)
        {
            MSG m2;
            while (PeekMessage(&m2, (HWND)0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
                m = m2;
        }
     ...
}

So I now have things working as i want them under windows.

ok, so my questions are:

(1) irrespective of my actual problem, should Juce perform mouse move compression (at least optionally) anyhow. eg as above.

(2) does anyone have a better way of fixing my problem.

thanks for your comments,
– hugh.


#2

Perhaps a nicer approach would be to start a timer (at a decent interval) to repaint your component on its mouseEnter(), and then stop the timer on its mouseExit().


#3

it’s actually the other components that need repainting! What i do is drag a component in front of other components who need repainting.

When i switch off my changes, everything still works, but the other components do not get repainted eagerly enough. When you reveal part of a component by moving away an obscuring component, the paint method on the revealed component does not get called quickly enough when you continue to drag.

This is an obvious optimisation to minimise needless repeat repainting, since the drag is ongoing. What i do think would be a better approach than mine, is to be able to adjust the delay or eagerness of the system to dispatch repaint during a drag.


#4

Hmm… Throwing away events by default would probably mess things up for some people…

I’m actually a bit surprised that the normal async paint mechanism isn’t working for you, because I’ve always found it to be pretty snappy. What is it you’re doing that’s so demanding?


#5

[quote=“jules”]Hmm… Throwing away events by default would probably mess things up for some people…

I’m actually a bit surprised that the normal async paint mechanism isn’t working for you, because I’ve always found it to be pretty snappy. What is it you’re doing that’s so demanding?[/quote]

I think he’s doing his own dragging and not going through the JUCE drag and drop mechanism. Jules if you recall, I had a related issue and you fixed it in the drag and drop loop. Maybe if he uses the JUCE drag and drop loop then his updating problem will go away.


#6

ok, here’s an example of what im doing:

[code]#include “…/JuceLibraryCode/JuceHeader.h”

static const char* poo =
“The number of pensioners is forecast to rise by half by 2030, and over-85s to double, resulting in a sharp increase in those with multiple long-term health conditions.\nThe 105-page report from the House of Lords’ public service and demography committee makes a series of recommendations for the Government, employers and wider society.\nIt says: “The UK population is ageing rapidly, but we have concluded that the Government and our society are woefully underprepared. Longer lives can be a great benefit, but there has been a collective failure to address the implications and without urgent action this great boon could turn into a series of miserable crises.” The committee comprises former Cabinet ministers, mandarins and medical experts, and its recommendations will be closely studied by the Government.\nLord Filkin, its chairman, said: “The public are entitled to an honest conversation about the implications.”\nThe committee welcomes plans to cap the maximum bill for those requiring social care but warns that the flagship announcement “will not be sufficient”.\nThey call for a “ten year” spending deal for the NHS and social care to allow services to be properly planned.\nThe report highlights predictions that more than half as many extra people will be living with three or more long-term conditions in England by 2018, compared with 2008.\nIt also warns that the Government, pensions industry and employers need to overhaul the pension system so that those in defined contribution schemes, now the majority of company schemes, have a clearer idea of what income they can eventually expect.\nThe peers also say that the state cannot shoulder all the burden and suggest that pensioners should be offered cheap, risk-free schemes to release equity from their homes.\nMichelle Mitchell, charity director general at Age UK, praised the report, saying: “It’s the first time a group of senior policymakers in this country has shown it grasps the scale and nature of change needed across our society in response to the gift of longer lives.””;

class Panel;

// the square is the object in front of the text
class Square: public Component
{
public:

Square(Panel* p) : _host(p) {}

void paint(Graphics& g)
{
    g.fillAll(Colours::red);
}

void mouseDown(const MouseEvent& e)
{
    _bounds = getBoundsInParent();
}

void mouseDrag(const MouseEvent& e);

Panel*              _host;
Rectangle<int>      _bounds;

};

// draw some text in the background begind the square
class SomeText: public TextEditor
{
public:

SomeText() { _init(); }

void append(char c)
{
    _s += c;
    setText(_s);
}

private:

void _init()
{
    setMultiLine(true);
    setScrollbarsShown(false);
    _font.setHeight(24);
    setFont(_font);

    _s = poo;
    setText(_s);
}

Font   _font;
String _s;

};

class Panel: public Component
{
public:
Panel() { _init(); }

void paint(Graphics& g)
{
    g.fillAll(Colours::whitesmoke);
}

void add(Component* c, int w, int h)
{
    c->setBounds(0,0,w,h);
    addAndMakeVisible(c);
}

void move(Component* c, const Rectangle<int>& b)
{   
    // side effect of moving the square is changing the text
    _stext->append('a');
    
    // update the position of the square
    c->setBounds(b);

    // lets chivvy up the painting
    // uncomment the next line to FORCE painting of the text
    // (providing you've hacked your windows event loop!)
    //_stext->getPeer()->performAnyPendingRepaintsNow();
}

private:

void _init()
{
    int w = 640;
    int h = 1024;

    _sq = new Square(this);
    _stext = new SomeText;

    add(_stext, w, h);
    add(_sq, 100,100);
}

Square*     _sq;
SomeText*   _stext;

};

void Square::mouseDrag(const MouseEvent& e)
{
Point dm = e.getOffsetFromDragStart();
_host->move(this, _bounds + dm);
}
[/code]

This code makes a Panel' withSomeText’ and a `Square’. The square can be moved in front of the text. in order to simulate the text component doing some work, the movement causes the text to change (add a single letter to the end).

When you run this, the red square moves very badly. hardly at all in fact, until you stop dragging, whence everything is fine.

However, if you comment in the last line of Panel::move, the square moves smoothly because im forcing through the repainting of the text.

so why do i need to do this? why arent the repaint calls being delivered to the text component in a timely way by default?

– hugh.


#7

Interesting.

Try a release build, running outside of VS!

I ran your snippet on my mac and it happily did 60fps, but on windows it was massively crap, like you said. BUT a release build runs pretty smoothly… And then, if you run a release build outside of Visual Studio, it goes even faster, easily doing full-speed updates without any hacks!

I think you’ve probably just got bitten by the notoriously slow MS debug allocator… Would be interesting to do some profiling and see what’s sucking the performance out!


#8

Crickey!

I’d already tried a release build, but it’s different outside of VS. Whoooah!

Without my event hack, it runs ok release outside of VS, as you said. it slows down after a bit, but i think that’s because the text buffer gets full of 'a’s.

Well, thanks very much for looking into this. I was worried this would be a problem on other platforms, but it looks like it’s a Windows debug/VS problem. I didnt know release builds were different outside of VS.

thanks,
– hugh.


#9

If your application throws many exceptions in short period of time, the Release build will become very slow when the Visual Studio debugger is attached.


#10

I’m not throwing exceptions here, but that would be another example of being misled when running under VS. VS must somehow have its hooks into the process. either that or it uses a different runtime that when run truely outside of VS.

hmm. something to watch out for then.


#11

[size=150]The Windows Heap Is Slow When Launched from the Debugger[/size]