Discussion:
Refresh not immediate on wxCocoa 3.0.0
Andreas Falkenhahn
2014-01-05 21:10:14 UTC
Permalink
Hi,

I noticed that it doesn't seem to be possible to force an immediate
refresh under wxCocoa 3.0.0. I've tried it with a wxScrolledWindow
class. I tried to force an immediate, synchronous execution of my
EVT_PAINT handler like this:

Refresh(false, NULL);
Update();

However, my EVT_PAINT handler is not executed before the next event
loop cycle. This causes issues because my program's logic expects
EVT_PAINT to be executed immediately and synchronously to the code
above.

Could somebody look into this? TIA!
--
Best regards,
Andreas Falkenhahn mailto:***@falkenhahn.com
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Vadim Zeitlin
2014-01-05 21:12:11 UTC
Permalink
On Sun, 5 Jan 2014 22:10:14 +0100 Andreas Falkenhahn wrote:

AF> I noticed that it doesn't seem to be possible to force an immediate
AF> refresh under wxCocoa 3.0.0. I've tried it with a wxScrolledWindow
AF> class. I tried to force an immediate, synchronous execution of my
AF> EVT_PAINT handler like this:
AF>
AF> Refresh(false, NULL);
AF> Update();
AF>
AF> However, my EVT_PAINT handler is not executed before the next event
AF> loop cycle. This causes issues because my program's logic expects
AF> EVT_PAINT to be executed immediately and synchronously to the code
AF> above.
AF>
AF> Could somebody look into this? TIA!

I'm far from being an expert in wxOSX internals but I think your logic
will need to be changed as I don't think it's possible to do what you want
under OS X at all.

Regards,
VZ
--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/
Andreas Falkenhahn
2014-01-05 21:24:59 UTC
Permalink
AF>> I noticed that it doesn't seem to be possible to force an immediate
AF>> refresh under wxCocoa 3.0.0. I've tried it with a wxScrolledWindow
AF>> class. I tried to force an immediate, synchronous execution of my
AF>> EVT_PAINT handler like this:

AF>> Refresh(false, NULL);
AF>> Update();

AF>> However, my EVT_PAINT handler is not executed before the next event
AF>> loop cycle. This causes issues because my program's logic expects
AF>> EVT_PAINT to be executed immediately and synchronously to the code
AF>> above.

AF>> Could somebody look into this? TIA!
Post by Vadim Zeitlin
I'm far from being an expert in wxOSX internals but I think your logic
will need to be changed as I don't think it's possible to do what you want
under OS X at all.
If it's really not possible then the wxWidgets API would need to be changed
or some caveat emptor be added because wxWindow::Update() currently guarantees
that the refresh is done immediately, cf.:

virtual void wxWindow::Update()

Calling this method immediately repaints the invalidated area of the window
and all of its children recursively (this normally only happens when the flow
of control returns to the event loop).

Notice that this function doesn't invalidate any area of the window so nothing
happens if nothing has been invalidated (i.e. marked as requiring a redraw).
Use Refresh() first if you want to immediately redraw the window unconditionally.

http://docs.wxwidgets.org/trunk/classwx_window.html#abaf28f1a075fd1b10f761a8febe597ec
--
Best regards,
Andreas Falkenhahn mailto:***@falkenhahn.com
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Stefan Csomor
2014-01-06 11:51:09 UTC
Permalink
Hi
Post by Andreas Falkenhahn
I noticed that it doesn't seem to be possible to force an immediate
refresh under wxCocoa 3.0.0. I've tried it with a wxScrolledWindow
class. I tried to force an immediate, synchronous execution of my
Refresh(false, NULL);
Update();
However, my EVT_PAINT handler is not executed before the next event
loop cycle. This causes issues because my program's logic expects
EVT_PAINT to be executed immediately and synchronously to the code
above.
Could somebody look into this? TIA!
painting out of band is very bad for performance on OSX, if you refresh
immediately too frequently you essentially block your app, which happened
for many wx apps, that slowed to a crawl on new machines ...

because we couldn't expect all old wx apps to have their code changes,
I've changed the code of wxNonOwnedWindow::Update to avoid this by
throttling to 30 Hz

void wxNonOwnedWindow::Update()
{
if ( clock() - s_lastFlush > CLOCKS_PER_SEC / 30 )
{
s_lastFlush = clock();
m_nowpeer->Update();
}
}

so this might bite you, but you should change your code to avoid tight
loops anyway, because this blocks the CPU


Best,

Stefan
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Vadim Zeitlin
2014-01-06 12:46:44 UTC
Permalink
On Mon, 6 Jan 2014 11:51:09 +0000 Stefan Csomor wrote:

SC> because we couldn't expect all old wx apps to have their code changes,
SC> I've changed the code of wxNonOwnedWindow::Update to avoid this by
SC> throttling to 30 Hz
SC>
SC> void wxNonOwnedWindow::Update()
SC> {
SC> if ( clock() - s_lastFlush > CLOCKS_PER_SEC / 30 )
SC> {
SC> s_lastFlush = clock();
SC> m_nowpeer->Update();
SC> }
SC> }

I think it would be better to remove the time check. Like this, you'll get
problems if you call Update() too often, and so will need to investigate if
your code is wrong. As it is now, you get problems even if you don't do
anything very wrong but just call Update() twice in a row (which surely
shouldn't be very catastrophic?).

In any case, if this code is here to stay, I agree that it should be
documented. But I think that it would be more reasonable to document wxOSX
limitations (i.e. that calling Update() in a tight loop is really bad) and
change the code.

Regards,
VZ
--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/
Andreas Falkenhahn
2014-01-06 23:45:40 UTC
Permalink
Post by Stefan Csomor
Hi
Post by Andreas Falkenhahn
I noticed that it doesn't seem to be possible to force an immediate
refresh under wxCocoa 3.0.0. I've tried it with a wxScrolledWindow
class. I tried to force an immediate, synchronous execution of my
Refresh(false, NULL);
Update();
However, my EVT_PAINT handler is not executed before the next event
loop cycle. This causes issues because my program's logic expects
EVT_PAINT to be executed immediately and synchronously to the code
above.
Could somebody look into this? TIA!
painting out of band is very bad for performance on OSX, if you refresh
immediately too frequently you essentially block your app, which happened
for many wx apps, that slowed to a crawl on new machines ...
Well, of course I'm using a timing mechanism myself that limits refreshes
to 50 times per second. Not sure if performance is really better with the
wxWidgets 30hz throttle because in order to make my code work with this
I'd need to change my code to asynchronous drawing which means that drawing
cannot be as optimized as with synchronous drawing where I have some
special cases for just clearing out certain areas, i.e. not really blitting
anything. This won't be easily adaptable to an asynchronous drawing model.
I think a backbuffer bitmap is the best strategy for asynchronous drawing
but I'm not sure if this really gives a better performance.

But if wxCocoa keeps this asynchronous drawing backend it should be
documented so that programmers are aware that Update() might not always
be synchronous.
--
Best regards,
Andreas Falkenhahn mailto:***@falkenhahn.com
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Andrew Trevorrow
2014-07-21 23:32:59 UTC
Permalink
On Monday, 6 January 2014 22:51:09 UTC+11, Stefan Csomor wrote:

I've changed the code of wxNonOwnedWindow::Update to avoid this by
Post by Stefan Csomor
throttling to 30 Hz
void wxNonOwnedWindow::Update()
{
if ( clock() - s_lastFlush > CLOCKS_PER_SEC / 30 )
{
s_lastFlush = clock();
m_nowpeer->Update();
}
}
I'd also like to request that the above throttling code be removed.
It's quite unnecessary and can break apps that want to do immediate
Update() calls in certain cases. I just wasted the last couple of days
tracking down and fixing this problem when trying to build our app
in wx3.0.1.
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Stefan Csomor
2014-07-24 06:49:35 UTC
Permalink
Hi Andrew
Post by Andrew Trevorrow
Post by Stefan Csomor
I've changed the code of wxNonOwnedWindow::Update to avoid this by
throttling to 30 Hz
void wxNonOwnedWindow::Update()
{
if ( clock() - s_lastFlush > CLOCKS_PER_SEC / 30 )
{
s_lastFlush = clock();
m_nowpeer->Update();
}
}
I'd also like to request that the above throttling code be removed.
It's quite unnecessary and can break apps that want to do immediate
Update() calls in certain cases.
That guard is not at all unnecessary, I wouldn¹t have written it
otherwise. The problem was that some apps are calling Update too
frequently - so that - without that patch - Update effectively throttles
them down to a crawl (perhaps 60 Hz of Update calls or so), because the
underlying Flush is blocking until the next OS refresh is taking place.
The fact that you run into missed update also means that your last call to
Update was too close.

But maybe we can find a better solution for that congestion problem. Are
you using the Carbon or the Cocoa build ? And what exactly needs immediate
update ?

Best,

Steafn
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Stefan Csomor
2014-07-24 07:11:47 UTC
Permalink
Hi

here's a TechNote as background information on underlying issue

https://developer.apple.com/library/mac/technotes/tn2133/_index.html

Stefan
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Andrew Trevorrow
2014-07-25 04:36:21 UTC
Permalink
Post by Andrew Trevorrow
Post by Stefan Csomor
I've changed the code of wxNonOwnedWindow::Update to avoid this by
throttling to 30 Hz
void wxNonOwnedWindow::Update()
{
if ( clock() - s_lastFlush > CLOCKS_PER_SEC / 30 )
{
s_lastFlush = clock();
m_nowpeer->Update();
}
}
I'd also like to request that the above throttling code be removed.
It's quite unnecessary and can break apps that want to do immediate
Update() calls in certain cases.
That guard is not at all unnecessary, I wouldn't have written it
otherwise.
It's unnecessary because the Mac OS automatically throttles update
calls that occur faster than the refresh rate (as mentioned in the
Tech Note you referred to in your next post).

Our app *relied* on that automatic throttling, so the guard code
you've added has now changed our app's behavior (for the worse).
See below for details.
The problem was that some apps are calling Update too
frequently - so that - without that patch - Update effectively throttles
them down to a crawl (perhaps 60 Hz of Update calls or so), because the
underlying Flush is blocking until the next OS refresh is taking place.
I'm not sure why you think 60Hz of updates is a crawl! If it's a
problem for some apps then they should be modified to do their own
update throttling. By modifying the behavior of wxWindow::Update()
you are changing the behavior of apps (like ours) that want it to do
exactly what the wx docs say: "immediately repaint the invalidated
area of the window". At the very least, it's inconsistent to have
Mac apps behave differently.
But maybe we can find a better solution for that congestion problem. Are
you using the Carbon or the Cocoa build ?
We currently do both: a Carbon build for Mac OS 10.4/5 and a Cocoa build
for 10.6+, but we'll probably drop the Carbon build in the next release.
And what exactly needs immediate update ?
Our app is a cellular automata simulator (Conway's Game of Life is
the best known example), so it needs to display pattern sequences
at various user-adjustable rates. We want the default rate to be
"display each new pattern as fast as possible". This is currently
achieved by the current loop (highly simplified!):

while generating patterns {
calculate next pattern
call window->Refresh()
call window->Update() (this should display results *immediately*)
call wxYield() to handle user events
}

The above code works perfectly fine with wxMSW and wxGTK, and with
wxMac 2.8, but with wxMac 3.0.1 many patterns are not seen due to
your guard code.

Now, I realize that this could be done differently (and better, to avoid
calling wxYield), but when I wrote the 1st version 9 years ago I was
new to wxWidgets and followed the example code in the Life demo which
has this loop:

case ID_TOPSPEED:
{
m_running = true;
m_topspeed = true;
UpdateUI();
while (m_running && m_topspeed)
{
OnStep();
wxYield();
}
break;
}

BTW, the Life demo is currently broken (and probably has been for some time).
It doesn't update patterns at all when the Start button is selected.
The simplest fix is to replace

m_canvas->DrawChanged();

in LifeFrame::OnStep() with these calls:

m_canvas->Refresh(false);
m_canvas->Update();

Andrew
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Stefan Csomor
2014-07-25 08:34:27 UTC
Permalink
Hi
Post by Andrew Trevorrow
I'm not sure why you think 60Hz of updates is a crawl! If it's a
problem for some apps then they should be modified to do their own
update throttling. By modifying the behavior of wxWindow::Update()
you are changing the behavior of apps (like ours) that want it to do
exactly what the wx docs say: "immediately repaint the invalidated
area of the window". At the very least, it's inconsistent to have
Mac apps behave differently.
the problem is that not only the updates are throttled, but the execution
itself, so the Update call essentially blocks all execution on the main
thread without the throttle, and can only be called about 60 times a
second. Since quite a lot of apps instead of an event loop, Refresh and
Paint handlers, have a tight loop for performance reasons somewhere in
their code and called Update in the loop, their performance became a lot
worse on OSX once coalescing updates were standard, especially on the
faster machines. So that wx-throttle was the lesser evil, because then
people stopped having the problem that their apps were a lot slower on OSX
than on the other systems. And there were a lot of them, so this fixed
many more problematic apps than it created new problems.

I¹ll look at the Life demo. Currently I¹m thinking about either adding an
explicit - known to be blocking - Update variant, or to add a flag in case
of a vetoed Update and look at that at wxYield time.

Best,

Stefan
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Stefan Csomor
2014-07-25 16:16:59 UTC
Permalink
Hi Andrew
Post by Andrew Trevorrow
BTW, the Life demo is currently broken (and probably has been for some time).
It doesn't update patterns at all when the Start button is selected.
The simplest fix is to replace
m_canvas->DrawChanged();
m_canvas->Refresh(false);
m_canvas->Update();
I changed DrawChanged for OSX to just call Refresh(), no Update was
necessary, as the wxYield call is enough to trigger the refresh, so there
might be some other difference, are you always calling wxYield() in your
loop ?

I¹ve also measured the difference when running a release build of Life

Dice Shaker on a defined grid size with current Update implementation:
2267 Generations per second
Dice Shaker on the same grid size with the Throttle Removed : 57
Generations per second

Best,

Stefan
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Andrew Trevorrow
2014-07-25 23:16:16 UTC
Permalink
Post by Stefan Csomor
I changed DrawChanged for OSX to just call Refresh(), no Update was
necessary, as the wxYield call is enough to trigger the refresh, so there
might be some other difference, are you always calling wxYield() in your
loop ?
Not every time -- we only call wxYield() about 10 times per sec.
Post by Stefan Csomor
I've also measured the difference when running a release build of Life
2267 Generations per second
Dice Shaker on the same grid size with the Throttle Removed : 57
Generations per second
Clearly the gens per sec will be faster if you don't display each generation!
But that's exactly what we want to avoid. Displaying the evolution of a
pattern at 57 gens per sec results in a nice smooth animation. By skipping
lots of generations the animation is way too jerky. (Our app provides
other more precise ways to skip generations if the user wants to speed up
pattern evolution.)

Anyway, if you don't want to remove the Update throttling I'd rather
you not change anything else. It's very easy for me to apply a patch
to remove the throttling.

Andrew
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Stefan Csomor
2014-07-27 13:48:53 UTC
Permalink
Hi
Post by Andrew Trevorrow
Post by Stefan Csomor
I changed DrawChanged for OSX to just call Refresh(), no Update was
necessary, as the wxYield call is enough to trigger the refresh, so there
might be some other difference, are you always calling wxYield() in your
loop ?
Not every time -- we only call wxYield() about 10 times per sec.
calling wxYield at a higher frequency - the one you intended for Update -
would also solve the problem on OSX, as wxYield will call the OnPaint
handler in precisely the optimal frequency on OSX, you wouldn¹t have to
call Update at all
Post by Andrew Trevorrow
Post by Stefan Csomor
I've also measured the difference when running a release build of Life
2267 Generations per second
Dice Shaker on the same grid size with the Throttle Removed : 57
Generations per second
Clearly the gens per sec will be faster if you don't display each generation!
yes, the reasons I gave the numbers was to show the detrimental effect of
coalescing updates on otherwise unchanged code which called Update in its
loop, many eg also IIRC Boinc / ***@Home were victims of this problem.
We¹ve discussed the implications at that time on the list, and it was
decided that user code shouldn¹t have to be changed, and we¹d have to deal
with this internally

Best,

Stefan
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

To unsubscribe, send email to wx-users+***@googlegroups.com
or visit http://groups.google.com/group/wx-users
Loading...