Merge lp:~smspillaz/compiz-core/compiz-core.fix_880707.2.test into lp:~vanvugt/compiz-core/fix-880707.2

Proposed by Sam Spilsbury
Status: Superseded
Proposed branch: lp:~smspillaz/compiz-core/compiz-core.fix_880707.2.test
Merge into: lp:~vanvugt/compiz-core/fix-880707.2
Diff against target: 1820 lines (+1273/-172) (has conflicts)
18 files modified
cmake/CompizPlugin.cmake (+14/-1)
plugins/composite/CMakeLists.txt (+2/-0)
plugins/composite/composite.xml.in (+5/-10)
plugins/composite/include/composite/composite.h (+5/-12)
plugins/composite/include/composite/fpslimiter.h (+38/-0)
plugins/composite/src/composite.cpp (+0/-29)
plugins/composite/src/paintscheduler.cpp (+189/-0)
plugins/composite/src/paintscheduler.h (+97/-0)
plugins/composite/src/privates.h (+19/-13)
plugins/composite/src/screen.cpp (+126/-90)
plugins/composite/tests/CMakeLists.txt (+1/-0)
plugins/composite/tests/paintscheduler/CMakeLists.txt (+34/-0)
plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c (+6/-0)
plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h (+4/-0)
plugins/composite/tests/paintscheduler/test-paintscheduler.cpp (+702/-0)
plugins/opengl/include/opengl/opengl.h (+3/-0)
plugins/opengl/src/privates.h (+4/-0)
plugins/opengl/src/screen.cpp (+24/-17)
Text conflict in cmake/CompizPlugin.cmake
Text conflict in plugins/composite/src/screen.cpp
To merge this branch: bzr merge lp:~smspillaz/compiz-core/compiz-core.fix_880707.2.test
Reviewer Review Type Date Requested Status
Daniel van Vugt Needs Fixing
Tim Penhey Pending
Review via email: mp+82961@code.launchpad.net

This proposal has been superseded by a proposal from 2012-01-20.

Description of the change

Separated out the composite plugin's paint scheduler into a separate class. Created a testcase using libdrm.

Expected output:

./plugins/composite/tests/paintscheduler/compiz_paintscheduler_test
DEBUG: attempting to load platform i915 ... failed
DEBUG: attempting to load platform radeon ... failed
DEBUG: attempting to load platform nouveau ... success
DEBUG: average vblank wait time was 33.51
DEBUG: average frame rate 29.8418 Hz
TEST: unthrottled vblank timings time 33.51 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0.365006
PASS: unthrottled vblank timings
DEBUG: average vblank wait time was 21.01
DEBUG: average frame rate 47.5964 Hz
TEST: no vsync 50 Hz refresh rate time 21.01 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0
PASS: no vsync 50 Hz refresh rate
DEBUG: attempting to load platform i915 ... failed
DEBUG: attempting to load platform radeon ... failed
DEBUG: attempting to load platform nouveau ... success
DEBUG: average vblank wait time was 33.7067
DEBUG: average frame rate 29.6677 Hz
TEST: vsync 50 Hz refresh rate time 33.7067 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0.47914
PASS: vsync 50 Hz refresh rate
DEBUG: average vblank wait time was 33.9467
DEBUG: average frame rate 29.458 Hz
TEST: no vsync 20 Hz refresh rate time 33.9467 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0
PASS: no vsync 20 Hz refresh rate
DEBUG: attempting to load platform i915 ... failed
DEBUG: attempting to load platform radeon ... failed
DEBUG: attempting to load platform nouveau ... success
DEBUG: average vblank wait time was 49.9367
DEBUG: average frame rate 20.0254 Hz
TEST: vsync 30 Hz refresh rate time 49.9367 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0.234331
PASS: vsync 30 Hz refresh rate
DEBUG: average vblank wait time was 11.0133
DEBUG: average frame rate 90.799 Hz
TEST: no vsync 100 Hz refresh rate time 11.0133 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0
PASS: no vsync 100 Hz refresh rate
DEBUG: attempting to load platform i915 ... failed
DEBUG: attempting to load platform radeon ... failed
DEBUG: attempting to load platform nouveau ... success
DEBUG: average vblank wait time was 16.89
DEBUG: average frame rate 59.2066 Hz
TEST: vsync 100 Hz refresh rate time 16.89 within threshold of 10
DEBUG: median absolute deviation of the periods was 0
DEBUG: st. dev of the phases was 0.359418
PASS: vsync 100 Hz refresh rate

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I think this proposal is going too far.

You started with my proposal which is: 345 lines (+65/-145) 3 files modified
and turned it into: 1175 lines (+758/-195) 12 files modified

If such a large volume of code is required just to add a test case, at the expense of future maintainability and readability, then I suggest the test case should not exist. Less code will lead to fewer bugs and better maintainability/readability in the long run.

review: Disapprove
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Also, in terms of class design my personal rule is "if the class name ends in 'er' then you probably need to redesign it". Generally if you can come up with classes that are nouns that represent actual conceptual objects, and not actions (not 'er'), then everything will fit together more elegantly.

Revision history for this message
Tim Penhey (thumper) wrote :

If the purpose of the test is to ensure correct behaviour, I can't see what the problem is.

Sure, adding tests adds code to the system, but if the purpose of that code is to make sure that the change does what it says it does, then it is good code.

In systems with complete test coverage, it is often usual to have more test code than the code that is being tested.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I agree test cases can be any size, and most of the new lines of code are just tests. It's mostly just the introduction of a scheduler class that is rubbing my design sensibilities the wrong way. Especially when compared to how relatively simple and elegant lp:~vanvugt/compiz-core/fix-880707.2 is presently.

Also, test-paintscheduler seems to depend on having DRM-based graphics drivers installed and graphics active on the build machine. Won't that only work for people building compiz in a graphical environment, and only work for people with open source graphics drivers?

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

>
> Also, test-paintscheduler seems to depend on having DRM-based graphics drivers
> installed and graphics active on the build machine. Won't that only work for
> people building compiz in a graphical environment, and only work for people
> with open source graphics drivers?

Unfortunately yes. I've updated the test proposal so that it just skips the vblanking test on hardware where DRM is available. I'll need to check the nvidia or ati documentation a little later on how to do hardware vblanks, but they might not expose such information.

The scheduler class is necessary so that we can unit test it. Its not possible to instansiate CompositeScreen outside of the compiz process since it requires core to be initialized.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

(Unfortunately the new upstream rules means that I can't merge this until there is a testcase for it. If you have any other ideas on testcases or class separations I'd be glad to take them :) The test can't be running inside compiz though)

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Also noted that I need to add some #ifdefs to skip the drm stuff if its not available.

2900. By Sam Spilsbury

Added tests for static refresh rates, finished DRM VBlank testing work

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> >
> > Also, test-paintscheduler seems to depend on having DRM-based graphics
> drivers
> > installed and graphics active on the build machine. Won't that only work for
> > people building compiz in a graphical environment, and only work for people
> > with open source graphics drivers?
>
> Unfortunately yes. I've updated the test proposal so that it just skips the
> vblanking test on hardware where DRM is available. I'll need to check the
> nvidia or ati documentation a little later on how to do hardware vblanks, but
> they might not expose such information.
>

That should read, where DRM is *not* available.

2901. By Sam Spilsbury

Don't throw inside the destructor

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

This is tricky. You can't actually test that you've fixed the vsync problems unless you test it using GLX vsync calls that the opengl plugin uses. And even then, you will not notice regressions caused by high latencies in plugins (unity for bug 880707) without also executing all the plugins' glPaintOutput methods etc.

So yes, the test cases probably test the new PaintScheduler class very well. But no, the tests have very little to do with verifying bug 880707 or any other vsync problems in reality. As such, I have to reject test-paintscheduler and PaintScheduler right now because they have nothing to do with verifying the fix for bug 880707 or any similar vsync regressions which could occur in plugins in future.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Also, the test cases seem to work by verifying the vblank period time with a tolerance of 10ms. Bug 880707 had nothing to do with the period/frequency, the period was always correct thanks to CompTimer. Bug 880707 was caused by poor _phase_ timing and the tolerance seems to be a max 2-3ms. It's testing the wrong code and using the wrong method... :(

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> Also, the test cases seem to work by verifying the vblank period time with a
> tolerance of 10ms. Bug 880707 had nothing to do with the period/frequency, the
> period was always correct thanks to CompTimer. Bug 880707 was caused by poor
> _phase_ timing and the tolerance seems to be a max 2-3ms. It's testing the
> wrong code and using the wrong method... :(

Thanks. I'll have a look into fixing it a little later this week then.

As for using GLX to do it, I'm open to that idea, although I wanted to form a testcase for this outside of opengl since there might be other uses for it.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> This is tricky. You can't actually test that you've fixed the vsync problems
> unless you test it using GLX vsync calls that the opengl plugin uses. And even
> then, you will not notice regressions caused by high latencies in plugins
> (unity for bug 880707) without also executing all the plugins' glPaintOutput
> methods etc.
>

Yes. This is a test for the PaintScheduler class. I don't think its a comprehensive test for bug 880707, and in reality, the fix for that is to write less slow plugins or move to some kind of multithreaded architecture (which I don't think we'll be doing any time soon) ;-)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The correct test cases for bug 880707 would look something like this:

1. Test composite's ability to adapt to slow plugins:
  (a) Enable hasVSync in opengl (or anywhere else like a tester plugin)
  (b) Call CompositeScreen::damageScreen/damageRegion several times.
  (c) Verify the time between (b) and CompositeScreen::paint being called is less than 2 milliseconds.

2. Test for slow plugins that would cause vsync problems if composite was not fixed:
  (a) Measure the time taken by the calls to glPaintOutput in PrivateGLScreen::paintOutputs.
  (b) Verify the time is less than 2 milliseconds.

I don't think it's realistic to expect any test cases like this before the fix for bug 880707 is committed. It would require a whole new test architecture based on a fully functioning compiz process.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Very importantly, the above test cases could be run with and without the fixes for bug 880707, to show the test cases themselves are correct.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> The correct test cases for bug 880707 would look something like this:
>
> 1. Test composite's ability to adapt to slow plugins:
> (a) Enable hasVSync in opengl (or anywhere else like a tester plugin)
> (b) Call CompositeScreen::damageScreen/damageRegion several times.
> (c) Verify the time between (b) and CompositeScreen::paint being called is
> less than 2 milliseconds.

That's fairly straightforward then. I'll write a case for that (although, the unit test that I wrote excersizes slightly similar codepaths, but it doesn't really excersize the case where repaints are queued based on work done by plugins rather than on a regular bases.).

I'll write a case for that.

>
> 2. Test for slow plugins that would cause vsync problems if composite was not
> fixed:
> (a) Measure the time taken by the calls to glPaintOutput in
> PrivateGLScreen::paintOutputs.
> (b) Verify the time is less than 2 milliseconds.

I think the idea here is that in cases where plugins are being slow, you move to the next half-rate of frame timing, so your refresh rate would be 30 Hz instead of 60 Hz if you start dropping below 60 Hz.

>
> I don't think it's realistic to expect any test cases like this before the fix
> for bug 880707 is committed. It would require a whole new test architecture
> based on a fully functioning compiz process.

I think it is safe to have at least, a testcase which shows defined behaviour for the frame timing at least, so that we have the ability to catch regressions of things change, no ?

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The frame rate doesn't halve, because the old composite code kind-of-partially adjusted for slow rendering. That would be a period/frequency problem but this was a problem with the phase. The core of bug 880707 was that composite was always out of phase with the hardware vblank, but using the right frequency/framerate:

Composite: --*----*----*----*----*----*
Hardware : *----*----*----*----*----*

So composite changed the front buffer roughly 1/4 of the way through the vrefresh, which was about 4ms too late. Hence in my case all the tearing is visible in the top half of the screen.

Please don't test for framerate in any test cases relating to bug 880707. It's not related.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Also, these proposed test cases should not require a 'scheduler' class at all... ?

It's better if the test cases and glue don't conflict when merged with the fix for bug 880707, so you can add and remove the fix to verify the test case passes with it, and fails without it.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

On Tue, Nov 22, 2011 at 8:45 PM, Daniel van Vugt <email address hidden> wrote:

> The frame rate doesn't halve, because the old composite code
> kind-of-partially adjusted for slow rendering. That would be a
> period/frequency problem but this was a problem with the phase. The core of
> bug 880707 was that composite was always out of phase with the hardware
> vblank, but using the right frequency/framerate:
>
> Composite: --*----*----*----*----*----*
> Hardware : *----*----*----*----*----*
>
> So composite changed the front buffer roughly 1/4 of the way through the
> vrefresh, which was about 4ms too late. Hence in my case all the tearing is
> visible in the top half of the screen.
>
>
Yes, so the test needs to be one where we measure time from the vblank
event to some function which would be like glXSwapBuffers. My understanding
is that the scheduler is supposed to work like:

Damage -> PreparePaint (timeDiff being the diff from the last Damage) ->
Paint Dispatch -> Wait vblank -> Update front buffer -> Check if another
paint needs to be dispatched immediately or sleep until one does.

I can adjust the tests to be based around that case to ensure that we can
schedule paints whenever we want to (random) but the actual part where we
update the front buffer never happens outside within 3ms outside of the
vblank event.

(My apologies for the delay, I want to make sure that I'm really sure of
the correct behaviour and that we can verify the correct behaviour before I
merge it)

> Please don't test for framerate in any test cases relating to bug 880707.
> It's not related.
>
>
I know it isn't strictly related here, however it is the correct behaviour
in this case (halving the frame rate rather than going below 60Hz which
would put you out of phase)

>
> --
>
> https://code.launchpad.net/~smspillaz/compiz-core/compiz-core.fix_880707.2.test/+merge/82961<https://code.launchpad.net/%7Esmspillaz/compiz-core/compiz-core.fix_880707.2.test/+merge/82961>
> You are the owner of
> lp:~smspillaz/compiz-core/compiz-core.fix_880707.2.test.
>

--
Sam Spilsbury

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

On Tue, Nov 22, 2011 at 8:57 PM, Daniel van Vugt <email address hidden> wrote:

> Also, these proposed test cases should not require a 'scheduler' class at
> all... ?
>
> It's better if the test cases and glue don't conflict when merged with the
> fix for bug 880707, so you can add and remove the fix to verify the test
> case passes with it, and fails without it.
>

The test case is there for catching regressions. I may need to change this
code in future. The reason for having a separate class is because this is
the architecture I'm trying to move compiz towards (eg, having separate
worker classes with fewer dependencies on the entire system so that
individual algorithms can be tested separately)

> --
>
> https://code.launchpad.net/~smspillaz/compiz-core/compiz-core.fix_880707.2.test/+merge/82961<https://code.launchpad.net/%7Esmspillaz/compiz-core/compiz-core.fix_880707.2.test/+merge/82961>
> You are the owner of
> lp:~smspillaz/compiz-core/compiz-core.fix_880707.2.test.
>

--
Sam Spilsbury

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Vblank is not an "event". It is an interval of time with a distinct beginning and an end. It could even be a millisecond or 2 in length. I think part of the cause of this bug is that glXWaitVideoSyncSGI returns immediately if we're already in vblank (with intel at least), meaning you have no idea how much more vblank time you have left to render the frame before tearing occurs. That's why my fix is aimed at starting the glXWaitVideoSyncSGI ASAP to ensure we are near the start of the vblanking interval before we update the front buffer.

[http://en.wikipedia.org/wiki/Vertical_blanking_interval]
[http://www.opengl.org/registry/specs/SGI/video_sync.txt]

I also note that if your test cases don't use GLX then you can remove the dependency on DRM. And even if you do use GLX then you should still remove the DRM code. Because we have no guarantee that the aforementioned behaviour of glXWaitVideoSyncSGI matches that of DRM. It's likely to even be different between driver manufacturers. So best to just use timers and no DRM code.

As for the style issues... please try to avoid naming classes with an 'er' suffix. Consider it a challenge. I think you'll find your resulting designs more logical and elegant if you do.

Please do take your time. The original fix took me several days solid of testing and refinement. Part of the reason why I'm distressed to see it get convoluted so quickly :(

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

An example of a better class name/design: Clutter uses a class called Timeline for scheduling animations...

http://www.gtkmm.org/docs/cluttermm-0.9/docs/reference/html/classClutter_1_1Timeline.html

Revision history for this message
Sam Spilsbury (smspillaz) wrote :
Download full text (4.7 KiB)

On Wed, Nov 23, 2011 at 11:24 AM, Daniel van Vugt <email address hidden> wrote:
>
> Vblank is not an "event". It is an interval of time with a distinct beginning and an end. It could even be a millisecond or 2 in length. I think part of the cause of this bug is that glXWaitVideoSyncSGI returns immediately if we're already in vblank (with intel at least), meaning you have no idea how much more vblank time you have left to render the frame before tearing occurs.

Ah. So on libdrm at least this is implemented as an event rather than
a kind of poll-waiting thing. I agree its awkward, though, this is the
only GL independent implementation I know of (wanted to test the logic
of the code outside of OpenGL).

>
> That's why my fix is aimed at starting the glXWaitVideoSyncSGI ASAP to ensure we are near the start of the vblanking interval before we update the front buffer.
>
> [http://en.wikipedia.org/wiki/Vertical_blanking_interval]
> [http://www.opengl.org/registry/specs/SGI/video_sync.txt]
>
> I also note that if your test cases don't use GLX then you can remove the dependency on DRM. And even if you do use GLX then you should still remove the DRM code. Because we have no guarantee that the aforementioned behaviour of glXWaitVideoSyncSGI matches that of DRM. It's likely to even be different between driver manufacturers. So best to just use timers and no DRM code.

I'd be more concerned actually if the implementation with
glXWaitVideoSyncSGI was different depending on the driver codebases -
it *should* block until the vblank period happens rather than doing
anything else. I recognize that on some drivers this would be a
problem like you said because if the vertical blanking period is
already underway then there will be no blocking and an immediate
return. (This is ironically where using libdrm would help somewhat,
because this code is setup in such a way that the paint dispatch
thread *always* blocks until the *next* vertical blanking period, even
if we were in one already, but obviously we can't do that because its
not portable).

This code some something similar (although in a somewhat more
convoluted way). It spawns a thread to watch libdrm events, and the
paint thread waits on the event watcher thread to wake it up whenever
the vblanking period starts (eg, kernel gave us a vblank event).

So it's something like:

Thread 1: Timer dispatch -> Calculate time diff -> preparePaint ->
paint -> wait for vblank ..............! -> fill front buffer ->
repeat
Thread 2: Handle libdrm event ... -> ... -> ...
-----------------------------------------------------------------
vblank -> ! -> repeat

Of course, now we're discussing the implementation detail of the testcase ;-)

libdrm here of course ,serves as a testcase to make sure that Thread 1
is doing the right thing. You could replace thread 2 with anything,
even a dummy thing which wakes up every * ms to allow Thread 1 to be
unblocked, simulating the vblank blocking period. What is important in
the testcase is that regardless of how many times Thread 1 needs to do
work, it is always doing the critical fill-front-buffer stage within
3ms of being woken up. If thread 1 is was taking forever to do its
work, then ...

Read more...

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Whoa, too many words. But I think the gist is that you understand :)

I will be verifying your next proposal by actually testing not only the bug is still fixed, but also that the test cases do actually verify bug 880707. I will be doing this by finding ways to reintroduce the bug and then verify your test cases fail under those conditions. And of course verifying that the test cases pass when the fix is enabled. Please save time by testing this yourself before proposing a new version...

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

It appears the half framerate problem does happen with some drivers. I can reproduce the half framerate issue with NVIDIA-280 on a GT210 card, and think that bug 888039 covers this. The good news is that (as expected) the half framerate issue is indeed solved by lp:~vanvugt/compiz-core/fix-880707.2. And the older cause of low NVIDIA frame rates - bug 92599 - seems to be solved (avoided) by the fix too.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

To clarify... with NVIDIA-280... the bug is very obvious and easy to see as half-frame-rate if you have enabled "Workarounds>Force full screen redraws (buffer swap) on repaint". If you don't have that enabled, then you are likely to mostly see tearing instead of a lower frame rate.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Will have another look at this tomorrow.

2902. By Sam Spilsbury

Merged from lp:~vanvugt/compiz-core/fix-880707.2 and added tests for vblank
phase too

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Please base this on my branch, and retarget for lp:compiz-core. While I'm happy to review and test it, I am probably not going to be totally confident having all these new changes committed in my name. So let's make it yours.

And please don't do the final submission until at least next week. Because this week is crazy and I'd rather not have the option of even thinking about it.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Also, please don't resubmit until the work in progress described in my proposal is completed.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Sure no problem.

One thing I was going to ask based on something I came across while testing this. The composite plugin has an option to throttle frame rates to some specified rate, eg 1 to 200 frames per second. However, with the current code this does not happen if the paint handler is doing vsync. I think it should be possible to limit the fps to a certain number while still doing vsync, but I'm wondering if this is the correct behaviour. Let me know when you have time to.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

It was intentional that the throttling doesn't work with vsync. Because you could easily break the nice smooth vsync by having a bad frame rate value detected or set manually. I didn't want anyone to shoot themselves in the foot and log new bugs because they had been fiddling with the wrong settings. If you want a specific frame rate other than that of the monitor then you need to turn vsync off. I don't think it's a bad thing...

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Ah, I was thinking more along the lines of:

If you have a monitor with a refresh rate of N, then the actual refresh rate with vsync on while throttled is going to be N / 2 until N falls under the maximum refresh rate. The phase in this case would still be in-sync with the monitor since the actual swapbuffers / copysubbuffermesa calls are going to happen within that 4ms window of the vertical blank period anyways.

If functionality like that doesn't make sense I'm happy to just drop it. The only use-case I can think of really is where the user intentionally wanted to throttle the compositor framerate in order to save battery life at the cost of smoother animations or video.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Right now it would cause a regression of bug 92599 which is not acceptable. That's one good reason to think about how to do it as a separate future enhancement. Even then it's not important at all.

I think the existing compiz CPU issues (mostly discussed in bug 803943) are more important with respect to power savings.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Ah right, the refresh rate detection. That isn't much fun. I think that refresh rate detection should be removed anyways and the "refresh rate" option should actually be a cap on how much your screen is allowed to redraw per second even with vsync on. And that number should be fairly high, since the vblank period of most monitors is usually around 16ms.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

(The high cpu usage in the case back then was mostly with unity when blurs were being updated constantly, I'm not really sure if that bug is particularly valid anymore, or at least, while testing things this time around the cpu usage while idle is nil and while active its around 5%)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

A maximum refresh rate option would be non-trivial to get right if you're also trying to sync to vblank. And if it can be treated as a separate activity, then I think it should be. We don't need this branch getting any more complicated than it already is.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

I got it working in about 100~ lines of code :) I can probably move it to a separate branch if need be. For now its in this one.

2903. By Sam Spilsbury

Added a software based paint scheduler

2904. By Sam Spilsbury

Added missing files

2905. By Sam Spilsbury

Don't create multiple vblank waiter objects

2906. By Sam Spilsbury

Generalized the testcase code into a separate function, allow testing
for throttled refresh rates both with and without vsync

2907. By Sam Spilsbury

Make the tests fail if the actual framerate exceeds the framerate the user
tried to throttle to

2908. By Sam Spilsbury

Expose vblanking in the paint scheduler base and support framerate limiting
with vblanking. Now it is possible to limit the framerate while still keeping
in sync with the monitor (the framerate will decrease roughly in factors of
two, eg 60Hz -> 30Hz -> 15Hz -> 7.5Hz etc)

2909. By Sam Spilsbury

Use some more accurate statistics in testing and remove refresh rate detection.

Refresh rate detection is relatively useless when you've got the hardware
already doing vsync for you, and some drivers incorrectly report what the
refresh rate is. Worse, with the ability now to have the refresh rate capped
at a certain framerate *while* vsyncing, if the detected refresh rate actually
falls beneath the vsync period, compiz will wait twice per frame in order
to stay under that value, which hammers performance.

Set a refresh rate cap of -1 to force vsync usage (even if your theoretical
driver allows absurdly small vsync periods). Otherwise the cap will be
applied to the current vsync rate.

2910. By Sam Spilsbury

Added a concept of "work factors" to the tests, eg, some unit of work that
might cause us to miss a vblank and slow us down, adjusted the tests to
make sure that we are always falling under the refresh rate cap, the period
is roughly the same and the phase never deviates past 1

2911. By Sam Spilsbury

Have a much lower tolerance threshold for phase deviation and allow libdrm
based vblank testing again

2912. By Sam Spilsbury

Merged lp:~vanvugt/compiz-core/fix-880707.2

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

100 lines of code is very large (and risky) for such a simple feature. Or am I misinterpreting something?

2913. By Sam Spilsbury

Force a redraw time of optimalRedrawTime if we moved from idle to active.

In cases where the plugins are requesting a redraw during donePaint or addWindowDamage,
we should always respect the absolute time difference because on slower systems, or where
double-vsyncs come into effect due to frame limiting, we would force a redraw time of optimalRedrawTime
which meant that plugins could not accurately skip frames, which would mean that the animations
would "appear" to be slower.

However, in cases where we are moving from idle to active, we must always force msSinceLastPaint
to be optimalRedrawTime since with the tickless algorithm, we are measuring the absolute difference
from the last time the screen was drawn to this time, and this difference could be very large.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

It was mostly a case of exposing the vsync api from the paint handlers to the paint scheduler, and requiring another vsync wait if we were exceeding the maximum frame rate. So mostly shuffling code around.

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Any more ideas on this Daniel? I can't merge your original branch without unit tests, but I can make a PPA of this if need be.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Yes, a PPA if possible please. I'm trying to reduce the amount of Ubuntu work I do. But I'm happy to test all the relevant bugs with a bunch of different graphics cards if you make it quick and easy for me :)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Though if your changes are compatible with the oneiric code, as mine are, then I can merge them myself... ?

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Hey Daniel,

Packages to test this can be found in ppa://smspillaz/880707

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Are you sure?...
"This PPA does not contain any packages yet. Find more information about how to upload packages in the PPA help page."

Revision history for this message
Sam Spilsbury (smspillaz) wrote :

Ah, typo when uploading, try it now https://launchpad.net/~smspillaz/+archive/880707

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The PPA is still missing the binary packages. It says they're not scheduled to start building for another 2 (amd64) and 8 (i386) hours respectively. That's strange... The builds should have been queued immediately. I will wait.

In future please ensure the binaries are built and tested before handing them over to someone else. It's always possible you can make simple mistakes (missing/wrong fixes), that ideally you want to find in the PPA before other people get stuck on them.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

A couple of conflicts, which weren't too hard to fix. But then it was full of build failures. Too many failures...

Please pull from my branch again, then fix the conflicts and build failures.

review: Needs Fixing
2914. By Sam Spilsbury

Merge and cleanup the definitions of swapControlSGI

Unmerged revisions

2914. By Sam Spilsbury

Merge and cleanup the definitions of swapControlSGI

2913. By Sam Spilsbury

Force a redraw time of optimalRedrawTime if we moved from idle to active.

In cases where the plugins are requesting a redraw during donePaint or addWindowDamage,
we should always respect the absolute time difference because on slower systems, or where
double-vsyncs come into effect due to frame limiting, we would force a redraw time of optimalRedrawTime
which meant that plugins could not accurately skip frames, which would mean that the animations
would "appear" to be slower.

However, in cases where we are moving from idle to active, we must always force msSinceLastPaint
to be optimalRedrawTime since with the tickless algorithm, we are measuring the absolute difference
from the last time the screen was drawn to this time, and this difference could be very large.

2912. By Sam Spilsbury

Merged lp:~vanvugt/compiz-core/fix-880707.2

2911. By Sam Spilsbury

Have a much lower tolerance threshold for phase deviation and allow libdrm
based vblank testing again

2910. By Sam Spilsbury

Added a concept of "work factors" to the tests, eg, some unit of work that
might cause us to miss a vblank and slow us down, adjusted the tests to
make sure that we are always falling under the refresh rate cap, the period
is roughly the same and the phase never deviates past 1

2909. By Sam Spilsbury

Use some more accurate statistics in testing and remove refresh rate detection.

Refresh rate detection is relatively useless when you've got the hardware
already doing vsync for you, and some drivers incorrectly report what the
refresh rate is. Worse, with the ability now to have the refresh rate capped
at a certain framerate *while* vsyncing, if the detected refresh rate actually
falls beneath the vsync period, compiz will wait twice per frame in order
to stay under that value, which hammers performance.

Set a refresh rate cap of -1 to force vsync usage (even if your theoretical
driver allows absurdly small vsync periods). Otherwise the cap will be
applied to the current vsync rate.

2908. By Sam Spilsbury

Expose vblanking in the paint scheduler base and support framerate limiting
with vblanking. Now it is possible to limit the framerate while still keeping
in sync with the monitor (the framerate will decrease roughly in factors of
two, eg 60Hz -> 30Hz -> 15Hz -> 7.5Hz etc)

2907. By Sam Spilsbury

Make the tests fail if the actual framerate exceeds the framerate the user
tried to throttle to

2906. By Sam Spilsbury

Generalized the testcase code into a separate function, allow testing
for throttled refresh rates both with and without vsync

2905. By Sam Spilsbury

Don't create multiple vblank waiter objects

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmake/CompizPlugin.cmake'
2--- cmake/CompizPlugin.cmake 2012-01-12 06:48:58 +0000
3+++ cmake/CompizPlugin.cmake 2012-01-20 06:45:01 +0000
4@@ -342,27 +342,40 @@
5 ${${_PLUGIN}_PKG_INCDIRS}
6 ${${_PLUGIN}_INCDIRS}
7 ${COMPIZ_INCLUDE_DIRS}
8- ${CMAKE_PREFIX_PATH}/include
9+ ${PLUGIN_PREFIX}/include
10 ${CMAKE_INCLUDE_PATH}
11 ${${_PLUGIN}_MOD_INCLUDE_DIRS}
12 ${CORE_MOD_INCLUDE_DIRS}
13 ${${_PLUGIN}_PLUGINDEP_MOD_INCLUDE_DIRS}
14 )
15
16+<<<<<<< TREE
17 get_property (${_PLUGIN}_MOD_LIBRARY_DIRS
18 GLOBAL
19 PROPERTY ${_PLUGIN}_MOD_LIBRARY_DIRS)
20
21+=======
22+ set (SYSTEM_LINK_DIRS
23+ "/usr/lib"
24+ "/usr/lib32"
25+ "/usr/lib64"
26+ "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
27+
28+>>>>>>> MERGE-SOURCE
29 link_directories (
30 ${COMPIZ_LINK_DIRS}
31 ${${_PLUGIN}_PKG_LIBDIRS}
32 ${${_PLUGIN}_LIBDIRS}
33 ${PLUGIN_LIBDIR}
34 ${COMPIZ_LIBDIR}/compiz
35+<<<<<<< TREE
36 ${CMAKE_PREFIX_PATH}/lib
37 ${CMAKE_PREFIX_PATH}/lib32
38 ${CMAKE_PREFIX_PATH}/lib64
39 ${${_PLUGIN}_MOD_LIBRARY_DIRS}
40+=======
41+ ${SYSTEM_LINK_DIRS}
42+>>>>>>> MERGE-SOURCE
43 )
44
45 add_library (
46
47=== modified file 'plugins/composite/CMakeLists.txt'
48--- plugins/composite/CMakeLists.txt 2011-03-11 12:15:30 +0000
49+++ plugins/composite/CMakeLists.txt 2012-01-20 06:45:01 +0000
50@@ -3,3 +3,5 @@
51 include (CompizPlugin)
52
53 compiz_plugin (composite)
54+
55+add_subdirectory (tests)
56
57=== modified file 'plugins/composite/composite.xml.in'
58--- plugins/composite/composite.xml.in 2009-08-08 06:00:14 +0000
59+++ plugins/composite/composite.xml.in 2012-01-20 06:45:01 +0000
60@@ -8,16 +8,11 @@
61 <_short>Slow Animations</_short>
62 <_long>Toggle use of slow animations</_long>
63 </option>
64- <option name="detect_refresh_rate" type="bool">
65- <_short>Detect Refresh Rate</_short>
66- <_long>Automatic detection of refresh rate</_long>
67- <default>true</default>
68- </option>
69- <option name="refresh_rate" type="int">
70- <_short>Refresh Rate</_short>
71- <_long>The rate at which the screen is redrawn (times/second)</_long>
72- <default>50</default>
73- <min>1</min>
74+ <option name="refresh_rate_cap" type="int">
75+ <_short>Refresh Rate Cap</_short>
76+ <_long>Maximum allowed refresh rate (set -1 to force VSync usage)</_long>
77+ <default>200</default>
78+ <min>-1</min>
79 <max>200</max>
80 </option>
81 <option name="unredirect_fullscreen_windows" type="bool">
82
83=== modified file 'plugins/composite/include/composite/composite.h'
84--- plugins/composite/include/composite/composite.h 2011-10-15 11:02:37 +0000
85+++ plugins/composite/include/composite/composite.h 2012-01-20 06:45:01 +0000
86@@ -35,6 +35,7 @@
87 #include <core/pluginclasshandler.h>
88 #include <core/timer.h>
89 #include <core/core.h>
90+#include <composite/fpslimiter.h>
91
92 #define COMPOSITE_SCREEN_DAMAGE_PENDING_MASK (1 << 0)
93 #define COMPOSITE_SCREEN_DAMAGE_REGION_MASK (1 << 1)
94@@ -78,14 +79,6 @@
95 */
96 #define PAINT_SCREEN_NO_BACKGROUND_MASK (1 << 6)
97
98-
99-typedef enum
100-{
101- CompositeFPSLimiterModeDisabled = 0,
102- CompositeFPSLimiterModeDefault,
103- CompositeFPSLimiterModeVSyncLike
104-} CompositeFPSLimiterMode;
105-
106 class PrivateCompositeScreen;
107 class PrivateCompositeWindow;
108 class CompositeScreen;
109@@ -104,6 +97,10 @@
110 const CompRegion &region) = 0;
111
112 virtual bool hasVSync () { return false; };
113+ /* Return true if we are allowed to wait some more
114+ * for framerate throttling */
115+ virtual bool waitVSync (unsigned int mask) { return false; }
116+ virtual void syncBuffers (unsigned int mask) = 0;
117
118 virtual void prepareDrawing () {};
119 virtual bool compositingActive () { return false; };
120@@ -171,10 +168,6 @@
121 public CompOption::Class
122 {
123 public:
124-
125-
126-
127- public:
128 CompositeScreen (CompScreen *s);
129 ~CompositeScreen ();
130
131
132=== added file 'plugins/composite/include/composite/fpslimiter.h'
133--- plugins/composite/include/composite/fpslimiter.h 1970-01-01 00:00:00 +0000
134+++ plugins/composite/include/composite/fpslimiter.h 2012-01-20 06:45:01 +0000
135@@ -0,0 +1,38 @@
136+/*
137+ * Copyright © 2008 Dennis Kasprzyk
138+ * Copyright © 2007 Novell, Inc.
139+ *
140+ * Permission to use, copy, modify, distribute, and sell this software
141+ * and its documentation for any purpose is hereby granted without
142+ * fee, provided that the above copyright notice appear in all copies
143+ * and that both that copyright notice and this permission notice
144+ * appear in supporting documentation, and that the name of
145+ * Dennis Kasprzyk not be used in advertising or publicity pertaining to
146+ * distribution of the software without specific, written prior permission.
147+ * Dennis Kasprzyk makes no representations about the suitability of this
148+ * software for any purpose. It is provided "as is" without express or
149+ * implied warranty.
150+ *
151+ * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
152+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
153+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
154+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
155+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
156+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
157+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158+ *
159+ * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
160+ * David Reveman <davidr@novell.com>
161+ */
162+
163+#ifndef _COMPIZ_COMPOSITE_FPSLIMITER_H
164+#define _COMPIZ_COMPOSITE_FPSLIMITER_H
165+
166+typedef enum
167+{
168+ CompositeFPSLimiterModeDisabled = 0,
169+ CompositeFPSLimiterModeDefault,
170+ CompositeFPSLimiterModeVSyncLike
171+} CompositeFPSLimiterMode;
172+
173+#endif
174
175=== modified file 'plugins/composite/src/composite.cpp'
176--- plugins/composite/src/composite.cpp 2009-03-15 17:37:45 +0000
177+++ plugins/composite/src/composite.cpp 2012-01-20 06:45:01 +0000
178@@ -55,35 +55,6 @@
179 }
180
181 bool
182-PrivateCompositeScreen::setOption (const CompString &name,
183- CompOption::Value &value)
184-{
185- unsigned int index;
186-
187- bool rv = CompositeOptions::setOption (name, value);
188-
189- if (!rv || !CompOption::findOption (getOptions (), name, &index))
190- return false;
191-
192- switch (index) {
193- case CompositeOptions::DetectRefreshRate:
194- if (optionGetDetectRefreshRate ())
195- detectRefreshRate ();
196- break;
197- case CompositeOptions::RefreshRate:
198- if (optionGetDetectRefreshRate ())
199- return false;
200- redrawTime = 1000 / optionGetRefreshRate ();
201- optimalRedrawTime = redrawTime;
202- break;
203- default:
204- break;
205- }
206-
207- return rv;
208-}
209-
210-bool
211 CompositePluginVTable::init ()
212 {
213 if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
214
215=== added file 'plugins/composite/src/paintscheduler.cpp'
216--- plugins/composite/src/paintscheduler.cpp 1970-01-01 00:00:00 +0000
217+++ plugins/composite/src/paintscheduler.cpp 2012-01-20 06:45:01 +0000
218@@ -0,0 +1,189 @@
219+/*
220+ * Copyright © 2008 Dennis Kasprzyk
221+ * Copyright © 2007 Novell, Inc.
222+ *
223+ * Permission to use, copy, modify, distribute, and sell this software
224+ * and its documentation for any purpose is hereby granted without
225+ * fee, provided that the above copyright notice appear in all copies
226+ * and that both that copyright notice and this permission notice
227+ * appear in supporting documentation, and that the name of
228+ * Dennis Kasprzyk not be used in advertising or publicity pertaining to
229+ * distribution of the software without specific, written prior permission.
230+ * Dennis Kasprzyk makes no representations about the suitability of this
231+ * software for any purpose. It is provided "as is" without express or
232+ * implied warranty.
233+ *
234+ * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
235+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
236+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
237+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
238+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
239+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
240+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
241+ *
242+ * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
243+ * David Reveman <davidr@novell.com>
244+ * Daniel van Vugt <vanvugt@gmail.com>
245+ * Sam Spilsbury <sam.spilsbury@canonical.com>
246+ */
247+
248+#include "paintscheduler.h"
249+#include <boost/bind.hpp>
250+
251+compiz::composite::scheduler::PaintScheduler::PaintScheduler (PaintSchedulerDispatchBase *b) :
252+ mSchedulerState (0),
253+ mRedrawTime (1000 / 50),
254+ mOptimalRedrawTime (1000 / 50),
255+ mFPSLimiterMode (CompositeFPSLimiterModeDefault),
256+ mDispatchBase (b)
257+{
258+ gettimeofday (&mLastRedraw, 0);
259+}
260+
261+compiz::composite::scheduler::PaintScheduler::~PaintScheduler ()
262+{
263+ mPaintTimer.stop ();
264+}
265+
266+bool
267+compiz::composite::scheduler::PaintScheduler::schedule ()
268+{
269+ int delay = 1;
270+
271+ if (!mDispatchBase->schedulerCompositingActive ())
272+ return false;
273+
274+ if (mSchedulerState & paintSchedulerPainting)
275+ {
276+ mSchedulerState |= paintSchedulerReschedule;
277+ return false;
278+ }
279+
280+ if (mSchedulerState & paintSchedulerScheduled)
281+ return false;
282+
283+ mSchedulerState |= paintSchedulerScheduled;
284+
285+ if (mFPSLimiterMode == CompositeFPSLimiterModeVSyncLike ||
286+ (mDispatchBase->schedulerHasVsync ()))
287+ {
288+ delay = 1;
289+ }
290+ else
291+ {
292+ struct timeval now;
293+ gettimeofday (&now, 0);
294+ int elapsed = TIMEVALDIFF (&now, &mLastRedraw);
295+ if (elapsed < 0)
296+ elapsed = 0;
297+ delay = elapsed < mOptimalRedrawTime ? mOptimalRedrawTime - elapsed + 1 : 1;
298+ }
299+ /*
300+ * Note the use of delay = 1 instead of 0, even though 0 would be better.
301+ * A delay of zero is presently broken due to CompTimer bugs;
302+ * 1. Infinite loop in CompTimeoutSource::callback when a zero
303+ * timer is set.
304+ * 2. Priority set too high in CompTimeoutSource::CompTimeoutSource
305+ * causing the glib main event loop to be starved of X events.
306+ * Fixes for both of these issues are being worked on separately.
307+ */
308+
309+ mPaintTimer.start
310+ (boost::bind (&compiz::composite::scheduler::PaintScheduler::dispatch, this),
311+ delay);
312+
313+ return false;
314+}
315+
316+int
317+compiz::composite::scheduler::PaintScheduler::getRedrawTime ()
318+{
319+ return mRedrawTime;
320+}
321+
322+int
323+compiz::composite::scheduler::PaintScheduler::getOptimalRedrawTime ()
324+{
325+ return mOptimalRedrawTime;
326+}
327+
328+bool
329+compiz::composite::scheduler::PaintScheduler::dispatch ()
330+{
331+ struct timeval tv;
332+ int timeDiff;
333+
334+ gettimeofday (&tv, 0);
335+
336+ timeDiff = TIMEVALDIFF (&tv, &mLastRedraw);
337+
338+ /* handle clock rollback */
339+
340+ if (timeDiff < 0)
341+ timeDiff = 0;
342+
343+ /*
344+ * Now that we use a "tickless" timing algorithm, timeDiff could be
345+ * very large if the screen is truely idle.
346+ * However plugins expect the old behaviour where timeDiff is never
347+ * larger than the frame rate (optimalRedrawTime).
348+ * So enforce this to keep animations timed correctly and smooth...
349+ */
350+
351+ if (timeDiff > mOptimalRedrawTime &&
352+ !(mSchedulerState & paintSchedulerReschedule))
353+ timeDiff = mOptimalRedrawTime;
354+
355+ mSchedulerState |= paintSchedulerPainting;
356+ mSchedulerState &= ~paintSchedulerReschedule;
357+
358+ mRedrawTime = timeDiff;
359+
360+ mDispatchBase->prepareScheduledPaint (timeDiff);
361+ mDispatchBase->paintScheduledPaint ();
362+
363+ mLastRedraw = tv;
364+
365+ /* Throttle to the allowed refresh rate */
366+ if (mFPSLimiterMode == CompositeFPSLimiterModeDefault)
367+ {
368+ if (mDispatchBase->schedulerHasVsync ())
369+ {
370+ do
371+ {
372+ /* If throttling is allowed, ensure that
373+ * timeDiff is always >= mOptimalRedrawTime
374+ * by waiting for another video-sync */
375+ if (mDispatchBase->syncScheduledPaint ())
376+ {
377+ struct timeval stv;
378+ gettimeofday (&stv, 0);
379+ timeDiff = TIMEVALDIFF (&stv, &mLastRedraw);
380+ }
381+ else
382+ timeDiff = mOptimalRedrawTime;
383+ }
384+ while (timeDiff < mOptimalRedrawTime);
385+ }
386+ }
387+ else if (mFPSLimiterMode == CompositeFPSLimiterModeVSyncLike)
388+ {
389+ if (mDispatchBase->schedulerHasVsync ())
390+ mDispatchBase->syncScheduledPaint ();
391+ }
392+
393+ mDispatchBase->doneScheduledPaint ();
394+ mSchedulerState &= ~(paintSchedulerPainting | paintSchedulerScheduled);
395+
396+ if (mSchedulerState & paintSchedulerReschedule)
397+ schedule ();
398+
399+ return false;
400+}
401+
402+void
403+compiz::composite::scheduler::PaintScheduler::setRefreshRate (unsigned int rate)
404+{
405+ mOptimalRedrawTime = mRedrawTime =
406+ static_cast <int> (1000 / static_cast <float> (rate));
407+}
408
409=== added file 'plugins/composite/src/paintscheduler.h'
410--- plugins/composite/src/paintscheduler.h 1970-01-01 00:00:00 +0000
411+++ plugins/composite/src/paintscheduler.h 2012-01-20 06:45:01 +0000
412@@ -0,0 +1,97 @@
413+/*
414+ * Copyright © 2008 Dennis Kasprzyk
415+ * Copyright © 2007 Novell, Inc.
416+ *
417+ * Permission to use, copy, modify, distribute, and sell this software
418+ * and its documentation for any purpose is hereby granted without
419+ * fee, provided that the above copyright notice appear in all copies
420+ * and that both that copyright notice and this permission notice
421+ * appear in supporting documentation, and that the name of
422+ * Dennis Kasprzyk not be used in advertising or publicity pertaining to
423+ * distribution of the software without specific, written prior permission.
424+ * Dennis Kasprzyk makes no representations about the suitability of this
425+ * software for any purpose. It is provided "as is" without express or
426+ * implied warranty.
427+ *
428+ * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
429+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
430+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
431+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
432+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
433+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
434+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
435+ *
436+ * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
437+ * David Reveman <davidr@novell.com>
438+ */
439+
440+#include <compiz.h>
441+#include <core/timer.h>
442+#include <composite/fpslimiter.h>
443+
444+#ifndef _COMPIZ_COMPOSITE_PAINTSCHEDULER_H
445+#define _COMPIZ_COMPOSITE_PAINTSCHEDULER_H
446+
447+namespace compiz
448+{
449+namespace composite
450+{
451+namespace scheduler
452+{
453+
454+const unsigned int paintSchedulerScheduled = 1 << 0;
455+const unsigned int paintSchedulerPainting = 1 << 1;
456+const unsigned int paintSchedulerReschedule = 1 << 2;
457+
458+class PaintSchedulerDispatchBase
459+{
460+ public:
461+
462+ virtual void prepareScheduledPaint (unsigned int timeDiff) = 0;
463+ virtual void paintScheduledPaint () = 0;
464+ virtual bool syncScheduledPaint () = 0;
465+ virtual void doneScheduledPaint () = 0;
466+ virtual bool schedulerCompositingActive () = 0;
467+ virtual bool schedulerHasVsync () = 0;
468+};
469+
470+class PaintScheduler
471+{
472+ public:
473+
474+ PaintScheduler (PaintSchedulerDispatchBase *);
475+ ~PaintScheduler ();
476+
477+ bool schedule ();
478+ void setFPSLimiterMode (CompositeFPSLimiterMode mode);
479+ CompositeFPSLimiterMode getFPSLimiterMode ();
480+
481+ int getRedrawTime ();
482+ int getOptimalRedrawTime ();
483+
484+ void setRefreshRate (unsigned int);
485+
486+ protected:
487+
488+ bool dispatch ();
489+
490+ private:
491+
492+ unsigned int mSchedulerState;
493+
494+ struct timeval mLastRedraw;
495+ int mRedrawTime;
496+ int mOptimalRedrawTime;
497+
498+ CompositeFPSLimiterMode mFPSLimiterMode;
499+
500+ CompTimer mPaintTimer;
501+
502+ PaintSchedulerDispatchBase *mDispatchBase;
503+};
504+
505+}
506+}
507+}
508+
509+#endif
510
511=== modified file 'plugins/composite/src/privates.h'
512--- plugins/composite/src/privates.h 2011-11-14 07:41:22 +0000
513+++ plugins/composite/src/privates.h 2012-01-20 06:45:01 +0000
514@@ -31,6 +31,8 @@
515 #include <composite/composite.h>
516 #include <core/atoms.h>
517
518+#include "paintscheduler.h"
519+
520 #include "composite_options.h"
521
522 #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR > 2
523@@ -41,17 +43,17 @@
524 extern CompPlugin::VTable *compositeVTable;
525
526 extern CompWindow *lastDamagedWindow;
527+
528
529 class PrivateCompositeScreen :
530 ScreenInterface,
531+ public compiz::composite::scheduler::PaintSchedulerDispatchBase,
532 public CompositeOptions
533 {
534 public:
535 PrivateCompositeScreen (CompositeScreen *cs);
536 ~PrivateCompositeScreen ();
537
538- bool setOption (const CompString &name, CompOption::Value &value);
539-
540 void outputChangeNotify ();
541
542 void handleEvent (XEvent *event);
543@@ -64,7 +66,19 @@
544
545 void detectRefreshRate ();
546
547- void scheduleRepaint ();
548+ protected:
549+
550+ void prepareScheduledPaint (unsigned int timeDiff);
551+
552+ void paintScheduledPaint ();
553+
554+ bool syncScheduledPaint ();
555+
556+ void doneScheduledPaint ();
557+
558+ bool schedulerCompositingActive ();
559+
560+ bool schedulerHasVsync ();
561
562 public:
563
564@@ -94,18 +108,10 @@
565
566 int overlayWindowCount;
567
568- struct timeval lastRedraw;
569- int redrawTime;
570- int optimalRedrawTime;
571- bool scheduled, painting, reschedule;
572-
573 bool slowAnimations;
574
575- CompTimer paintTimer;
576-
577- compiz::composite::PaintHandler *pHnd;
578-
579- CompositeFPSLimiterMode FPSLimiterMode;
580+ compiz::composite::PaintHandler *pHnd;
581+ compiz::composite::scheduler::PaintScheduler scheduler;
582
583 CompWindowList withDestroyedWindows;
584 };
585
586=== modified file 'plugins/composite/src/screen.cpp'
587--- plugins/composite/src/screen.cpp 2012-01-16 09:50:28 +0000
588+++ plugins/composite/src/screen.cpp 2012-01-20 06:45:01 +0000
589@@ -175,16 +175,6 @@
590 }
591 }
592 }
593- else if (randrExtension &&
594- event->type == randrEvent + RRScreenChangeNotify)
595- {
596- XRRScreenChangeNotifyEvent *rre;
597-
598- rre = (XRRScreenChangeNotifyEvent *) event;
599-
600- if (screen->root () == rre->root)
601- detectRefreshRate ();
602- }
603 break;
604 }
605 }
606@@ -259,8 +249,6 @@
607
608 CompositeScreen::~CompositeScreen ()
609 {
610- priv->paintTimer.stop ();
611-
612 #ifdef USE_COW
613 if (useCow)
614 XCompositeReleaseOverlayWindow (screen->dpy (),
615@@ -279,21 +267,17 @@
616 exposeRects (),
617 windowPaintOffset (0, 0),
618 overlayWindowCount (0),
619- redrawTime (1000 / 50),
620- optimalRedrawTime (1000 / 50),
621- scheduled (false),
622- painting (false),
623- reschedule (false),
624 slowAnimations (false),
625 pHnd (NULL),
626- FPSLimiterMode (CompositeFPSLimiterModeDefault),
627+ scheduler (this),
628 withDestroyedWindows ()
629 {
630- gettimeofday (&lastRedraw, 0);
631 // wrap outputChangeNotify
632 ScreenInterface::setHandler (screen);
633
634 optionSetSlowAnimationsKeyInitiate (CompositeScreen::toggleSlowAnimations);
635+ optionSetRefreshRateCapNotify (boost::bind (&PrivateCompositeScreen::detectRefreshRate,
636+ this));
637 }
638
639 PrivateCompositeScreen::~PrivateCompositeScreen ()
640@@ -445,7 +429,6 @@
641 CompositeRedirectManual);
642
643 priv->pHnd = NULL;
644- priv->paintTimer.stop ();
645
646 hideOutputWindow ();
647 }
648@@ -464,7 +447,7 @@
649 {
650 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
651 priv->damageMask &= ~COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
652- priv->scheduleRepaint ();
653+ priv->scheduler.schedule ();
654 }
655
656 void
657@@ -483,14 +466,14 @@
658
659 if (priv->damage.numRects () > 100)
660 damageScreen ();
661- priv->scheduleRepaint ();
662+ priv->scheduler.schedule ();
663 }
664
665 void
666 CompositeScreen::damagePending ()
667 {
668 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_PENDING_MASK;
669- priv->scheduleRepaint ();
670+ priv->scheduler.schedule ();
671 }
672
673 unsigned int
674@@ -635,104 +618,154 @@
675 void
676 PrivateCompositeScreen::detectRefreshRate ()
677 {
678- if (!noDetection &&
679- optionGetDetectRefreshRate ())
680- {
681- CompString name;
682- CompOption::Value value;
683-
684- value.set ((int) 0);
685-
686- if (screen->XRandr ())
687- {
688- XRRScreenConfiguration *config;
689-
690- config = XRRGetScreenInfo (screen->dpy (),
691- screen->root ());
692- value.set ((int) XRRConfigCurrentRate (config));
693-
694- XRRFreeScreenConfigInfo (config);
695- }
696-
697- if (value.i () == 0)
698- value.set ((int) 50);
699-
700- mOptions[CompositeOptions::DetectRefreshRate].value ().set (false);
701- screen->setOptionForPlugin ("composite", "refresh_rate", value);
702- mOptions[CompositeOptions::DetectRefreshRate].value ().set (true);
703- optimalRedrawTime = redrawTime = 1000 / value.i ();
704- }
705+ unsigned int rate = optionGetRefreshRateCap ();
706+
707+ if (rate)
708+ scheduler.setRefreshRate (rate);
709 else
710- {
711- redrawTime = 1000 / optionGetRefreshRate ();
712- optimalRedrawTime = redrawTime;
713- }
714+ scheduler.setFPSLimiterMode (CompositeFPSLimiterModeVSyncLike);
715 }
716
717 CompositeFPSLimiterMode
718 CompositeScreen::FPSLimiterMode ()
719 {
720- return priv->FPSLimiterMode;
721+ return priv->scheduler.getFPSLimiterMode ();
722 }
723
724 void
725 CompositeScreen::setFPSLimiterMode (CompositeFPSLimiterMode newMode)
726 {
727- priv->FPSLimiterMode = newMode;
728-}
729-
730-void
731-PrivateCompositeScreen::scheduleRepaint ()
732-{
733- if (painting)
734- {
735- reschedule = true;
736- return;
737- }
738-
739- if (scheduled)
740- return;
741-
742- scheduled = true;
743-
744- int delay;
745- if (FPSLimiterMode == CompositeFPSLimiterModeVSyncLike ||
746- (pHnd && pHnd->hasVSync ()))
747- {
748- delay = 1;
749+ priv->scheduler.setFPSLimiterMode (newMode);
750+}
751+
752+void
753+PrivateCompositeScreen::prepareScheduledPaint (unsigned int timeDiff)
754+{
755+ if (pHnd)
756+ pHnd->prepareDrawing ();
757+
758+ cScreen->preparePaint (slowAnimations ? 1 : timeDiff);
759+}
760+
761+bool
762+PrivateCompositeScreen::schedulerCompositingActive ()
763+{
764+ return pHnd ? pHnd->compositingActive () : false;
765+}
766+
767+bool
768+PrivateCompositeScreen::schedulerHasVsync ()
769+{
770+ return pHnd ? pHnd->hasVSync () : false;
771+}
772+
773+void
774+PrivateCompositeScreen::paintScheduledPaint ()
775+{
776+ int mask;
777+
778+ /* substract top most overlay window region */
779+ if (overlayWindowCount)
780+ {
781+ for (CompWindowList::reverse_iterator rit =
782+ screen->windows ().rbegin ();
783+ rit != screen->windows ().rend (); rit++)
784+ {
785+ CompWindow *w = (*rit);
786+
787+ if (w->destroyed () || w->invisible ())
788+ continue;
789+
790+ if (!CompositeWindow::get (w)->redirected ())
791+ damage -= w->region ();
792+
793+ break;
794+ }
795+
796+ if (damageMask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
797+ {
798+ damageMask &= ~COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
799+ damageMask |= COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
800+ }
801+ }
802+
803+ tmpRegion = damage & screen->region ();
804+
805+ if (damageMask & COMPOSITE_SCREEN_DAMAGE_REGION_MASK)
806+ {
807+ if (tmpRegion == screen->region ())
808+ cScreen->damageScreen ();
809+ }
810+
811+ mask = damageMask;
812+
813+ damage = CompRegion ();
814+ damageMask = 0;
815+
816+ CompOutput::ptrList outputs (0);
817+
818+ if (optionGetForceIndependentOutputPainting () ||
819+ !screen->hasOverlappingOutputs ())
820+ {
821+ foreach (CompOutput &o, screen->outputDevs ())
822+ outputs.push_back (&o);
823 }
824 else
825+ outputs.push_back (&screen->fullscreenOutput ());
826+
827+ cScreen->paint (outputs, mask);
828+
829+ damageMask = mask;
830+}
831+
832+bool
833+PrivateCompositeScreen::syncScheduledPaint ()
834+{
835+ if (pHnd)
836+ return pHnd->waitVSync (damageMask);
837+
838+ return false;
839+}
840+
841+void
842+PrivateCompositeScreen::doneScheduledPaint ()
843+{
844+ if (pHnd)
845+ pHnd->syncBuffers (damageMask);
846+
847+ damageMask = 0;
848+
849+ cScreen->donePaint ();
850+
851+ foreach (CompWindow *w, screen->windows ())
852 {
853+<<<<<<< TREE
854 struct timeval now;
855 gettimeofday (&now, 0);
856 int elapsed = compiz::core::timer::timeval_diff (&now, &lastRedraw);
857 if (elapsed < 0)
858 elapsed = 0;
859 delay = elapsed < optimalRedrawTime ? optimalRedrawTime - elapsed : 1;
860+=======
861+ if (w->destroyed ())
862+ {
863+ CompositeWindow::get (w)->addDamage ();
864+ break;
865+ }
866+>>>>>>> MERGE-SOURCE
867 }
868- /*
869- * Note the use of delay = 1 instead of 0, even though 0 would be better.
870- * A delay of zero is presently broken due to CompTimer bugs;
871- * 1. Infinite loop in CompTimeoutSource::callback when a zero
872- * timer is set.
873- * 2. Priority set too high in CompTimeoutSource::CompTimeoutSource
874- * causing the glib main event loop to be starved of X events.
875- * Fixes for both of these issues are being worked on separately.
876- */
877- paintTimer.start
878- (boost::bind (&CompositeScreen::handlePaintTimeout, cScreen),
879- delay);
880 }
881
882 int
883 CompositeScreen::redrawTime ()
884 {
885- return priv->redrawTime;
886+ return priv->scheduler.getRedrawTime ();
887 }
888
889 int
890 CompositeScreen::optimalRedrawTime ()
891 {
892+<<<<<<< TREE
893 return priv->optimalRedrawTime;
894 }
895
896@@ -841,6 +874,9 @@
897 priv->scheduleRepaint ();
898
899 return false;
900+=======
901+ return priv->scheduler.getOptimalRedrawTime ();
902+>>>>>>> MERGE-SOURCE
903 }
904
905 void
906
907=== added directory 'plugins/composite/tests'
908=== added file 'plugins/composite/tests/CMakeLists.txt'
909--- plugins/composite/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
910+++ plugins/composite/tests/CMakeLists.txt 2012-01-20 06:45:01 +0000
911@@ -0,0 +1,1 @@
912+add_subdirectory (paintscheduler)
913
914=== added directory 'plugins/composite/tests/paintscheduler'
915=== added file 'plugins/composite/tests/paintscheduler/CMakeLists.txt'
916--- plugins/composite/tests/paintscheduler/CMakeLists.txt 1970-01-01 00:00:00 +0000
917+++ plugins/composite/tests/paintscheduler/CMakeLists.txt 2012-01-20 06:45:01 +0000
918@@ -0,0 +1,34 @@
919+include (FindPkgConfig)
920+
921+pkg_check_modules (COMPIZ_PAINTSCHEDULER REQUIRED dri libdrm glibmm-2.4)
922+
923+find_library( DRM_LIBRARY drm PATHS /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} NO_DEFAULT_PATH )
924+
925+include_directories (${COMPIZ_INCLUDE_DIRS}
926+ ${COMPIZ_PAINTSCHEDULER_INCLUDE_DIRS}
927+ ${compiz_SOURCE_DIR}/include
928+ ${compiz_BINARY_DIR}
929+ ${compiz_BINARY_DIR}/generated
930+ ${compiz_SOURCE_DIR}/src
931+ ../../include
932+ ../../src)
933+
934+#message (${DRM_LIBRARY})
935+
936+# FIXME: Hardcoding drm like this is stupid
937+# but due to a bug in CMake we have to do it
938+add_executable (compiz_paintscheduler_test
939+ ../../src/paintscheduler.cpp
940+ ${compiz_SOURCE_DIR}/src/timer.cpp
941+ ${compiz_SOURCE_DIR}/src/timeouthandler.cpp
942+ test-paintscheduler.cpp
943+ test-paintscheduler-set-drmvblanktype.c)
944+
945+target_link_libraries (compiz_paintscheduler_test
946+ ${COMPIZ_LIBRARIES} m pthread dl ${DRM_LIBRARY})
947+
948+set_target_properties (compiz_paintscheduler_test PROPERTIES
949+ LINK_FLAGS "-L/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
950+
951+add_test (test-paintscheduler
952+ ${CMAKE_CURRENT_BINARY_DIR}/compiz_paintscheduler_test)
953
954=== added file 'plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c'
955--- plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c 1970-01-01 00:00:00 +0000
956+++ plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c 2012-01-20 06:45:01 +0000
957@@ -0,0 +1,6 @@
958+#include "test-paintscheduler-set-drmvblanktype.h"
959+
960+void setDRMVBlankType (drmVBlankSeqType *t, unsigned int f)
961+{
962+ *t = f;
963+}
964
965=== added file 'plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h'
966--- plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h 1970-01-01 00:00:00 +0000
967+++ plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h 2012-01-20 06:45:01 +0000
968@@ -0,0 +1,4 @@
969+#include <xf86drm.h>
970+
971+void
972+setDRMVBlankType (drmVBlankSeqType *t, unsigned int f);
973
974=== added file 'plugins/composite/tests/paintscheduler/test-paintscheduler.cpp'
975--- plugins/composite/tests/paintscheduler/test-paintscheduler.cpp 1970-01-01 00:00:00 +0000
976+++ plugins/composite/tests/paintscheduler/test-paintscheduler.cpp 2012-01-20 06:45:01 +0000
977@@ -0,0 +1,702 @@
978+/*
979+ * Copyright © 2011 Canonical Ltd.
980+ *
981+ * Permission to use, copy, modify, distribute, and sell this software
982+ * and its documentation for any purpose is hereby granted without
983+ * fee, provided that the above copyright notice appear in all copies
984+ * and that both that copyright notice and this permission notice
985+ * appear in supporting documentation, and that the name of
986+ * Dennis Kasprzyk not be used in advertising or publicity pertaining to
987+ * distribution of the software without specific, written prior permission.
988+ * Dennis Kasprzyk makes no representations about the suitability of this
989+ * software for any purpose. It is provided "as is" without express or
990+ * implied warranty.
991+ *
992+ * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
993+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
994+ * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
995+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
996+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
997+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
998+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
999+ *
1000+ * drm_vblank handling code adapted from libdrm
1001+ * Copyright 2008 Tungsten Graphics
1002+ * Jakob Bornecrantz <jakob@tungstengraphics.com>
1003+ * Copyright 2008 Intel Corporation
1004+ * Jesse Barnes <jesse.barnes@intel.com>
1005+ *
1006+ * Authors: Sam Spilsbury <sam.spilsbury@canonical.com>
1007+ */
1008+
1009+#include <iostream>
1010+#include <string>
1011+#include <glib.h>
1012+#include <core/timeouthandler.h>
1013+#include <paintscheduler.h>
1014+#include <privatetimeouthandler.h>
1015+#include <privatetimer.h>
1016+#include <privatetimeoutsource.h>
1017+
1018+#include <assert.h>
1019+#include <stdio.h>
1020+#include <stdlib.h>
1021+#include <stdint.h>
1022+#include <unistd.h>
1023+#include <string.h>
1024+#include <errno.h>
1025+#include <sys/poll.h>
1026+#include <sys/time.h>
1027+
1028+#include <cmath>
1029+
1030+extern "C"
1031+{
1032+#include "test-paintscheduler-set-drmvblanktype.h"
1033+}
1034+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
1035+
1036+using namespace compiz::composite::scheduler;
1037+
1038+class DummyPaintDispatch;
1039+
1040+void pass (const std::string &message)
1041+{
1042+ std::cout << "PASS: " << message << std::endl;
1043+}
1044+
1045+void fail (const std::string &message)
1046+{
1047+ std::cout << "FAIL: " << message << std::endl;
1048+ exit (1);
1049+}
1050+
1051+class Worker
1052+{
1053+ public:
1054+
1055+ /* Amount of time it takes to complete some work in ms */
1056+ static void setWorkFactor (unsigned int workFactor) { mWorkFactor = workFactor; }
1057+ static void work ()
1058+ {
1059+ struct timespec ts;
1060+
1061+ ts.tv_sec = 0;
1062+ ts.tv_nsec = 1000000 * mWorkFactor;
1063+
1064+ nanosleep (&ts, NULL);
1065+ }
1066+
1067+ private:
1068+
1069+ static unsigned int mWorkFactor;
1070+};
1071+
1072+unsigned int Worker::mWorkFactor = 0;
1073+
1074+class VBlankWaiter
1075+{
1076+ public:
1077+
1078+ typedef timeval time_value;
1079+
1080+ VBlankWaiter (DummyPaintDispatch *owner, unsigned int n);
1081+ virtual ~VBlankWaiter ();
1082+
1083+ virtual bool waitVBlank () = 0;
1084+ virtual bool hasVSync () = 0;
1085+
1086+ bool checkTimings (float req, float threshold);
1087+ float averagePeriod ();
1088+ float averagePhase ();
1089+
1090+ bool addPaintStart (unsigned int time);
1091+ void addPaintDone (const VBlankWaiter::time_value &);
1092+
1093+ protected:
1094+
1095+ void addVBlank (const VBlankWaiter::time_value &);
1096+
1097+ std::vector <float> periods;
1098+ std::vector <VBlankWaiter::time_value> paintPeriods;
1099+ std::vector <VBlankWaiter::time_value> blankPeriods;
1100+ unsigned int timingCount;
1101+ unsigned int paintCount;
1102+ DummyPaintDispatch *owner;
1103+};
1104+
1105+class DRMVBlankWaiter :
1106+ public VBlankWaiter
1107+{
1108+ public:
1109+
1110+ DRMVBlankWaiter (DummyPaintDispatch *owner, unsigned int n);
1111+ ~DRMVBlankWaiter ();
1112+
1113+ bool waitVBlank ();
1114+ bool hasVSync ();
1115+
1116+ class DRMInfo
1117+ {
1118+ public:
1119+
1120+ DRMInfo (DRMVBlankWaiter *w) :
1121+ self (w)
1122+ {
1123+ }
1124+
1125+ DRMVBlankWaiter *self;
1126+ };
1127+
1128+ protected:
1129+
1130+ static gpointer
1131+ drmEventHandlerThread (gpointer);
1132+
1133+ static void
1134+ vblankHandler (int fd,
1135+ unsigned int frame,
1136+ unsigned int sec,
1137+ unsigned int usec,
1138+ void *data);
1139+ private:
1140+
1141+ drmVBlank mVbl;
1142+ drmEventContext evctx;
1143+ int drmFD;
1144+ DRMInfo *info;
1145+ GThread *eventThread;
1146+ GMutex eventMutex;
1147+ GCond eventCondition;
1148+ bool pendingVBlank;
1149+};
1150+
1151+class SleepVBlankWaiter :
1152+ public VBlankWaiter
1153+{
1154+ public:
1155+
1156+ SleepVBlankWaiter (DummyPaintDispatch *, unsigned int);
1157+ ~SleepVBlankWaiter ();
1158+
1159+ bool waitVBlank ();
1160+ bool hasVSync () { return true; }
1161+
1162+ static VBlankWaiter::time_value start_time;
1163+
1164+ private:
1165+
1166+ const static int vblank_ms_period = 60;
1167+};
1168+
1169+VBlankWaiter::time_value SleepVBlankWaiter::start_time;
1170+
1171+class NilVBlankWaiter :
1172+ public VBlankWaiter
1173+{
1174+ public:
1175+
1176+ NilVBlankWaiter (DummyPaintDispatch *, unsigned int);
1177+ ~NilVBlankWaiter ();
1178+
1179+ bool waitVBlank ();
1180+
1181+ bool hasVSync () { return false; }
1182+};
1183+
1184+class DummyPaintDispatch :
1185+ public PaintSchedulerDispatchBase
1186+{
1187+ public:
1188+
1189+ DummyPaintDispatch (Glib::RefPtr <Glib::MainLoop> loop);
1190+
1191+ void prepareScheduledPaint (unsigned int timeDiff)
1192+ {
1193+ success = waiter->addPaintStart (timeDiff);
1194+ }
1195+
1196+ void paintScheduledPaint ()
1197+ {
1198+ Worker::work ();
1199+ }
1200+
1201+ bool syncScheduledPaint ()
1202+ {
1203+ if (waiter && !isDone ())
1204+ return waiter->waitVBlank ();
1205+ else
1206+ return false;
1207+ }
1208+
1209+ void doneScheduledPaint ()
1210+ {
1211+ VBlankWaiter::time_value tv;
1212+ gettimeofday (&tv, NULL);
1213+
1214+ waiter->addPaintDone (tv);
1215+
1216+ if (waiter && success)
1217+ sched.schedule ();
1218+ else
1219+ ml->quit ();
1220+ }
1221+
1222+ bool schedulerCompositingActive ()
1223+ {
1224+ return true;
1225+ }
1226+
1227+ bool schedulerHasVsync ()
1228+ {
1229+ if (isDone ())
1230+ return false;
1231+
1232+ if (waiter)
1233+ return waiter->hasVSync ();
1234+
1235+ return true;
1236+ }
1237+
1238+ void setWaiter (VBlankWaiter *w)
1239+ {
1240+ if (waiter)
1241+ delete waiter;
1242+
1243+ waiter = w;
1244+ }
1245+
1246+ void setRefreshRate (unsigned int r)
1247+ {
1248+ sched.setRefreshRate (r);
1249+ }
1250+
1251+ bool isDone () { return !(waiter && success); }
1252+
1253+ private:
1254+
1255+ PaintScheduler sched;
1256+ VBlankWaiter *waiter;
1257+ bool success;
1258+ Glib::RefPtr <Glib::MainLoop> ml;
1259+};
1260+
1261+float
1262+VBlankWaiter::averagePeriod ()
1263+{
1264+ float buf = 0.0f;
1265+
1266+ for (std::vector <float>::iterator it = periods.begin ();
1267+ it != periods.end (); it++)
1268+ {
1269+ buf += (*it);
1270+ }
1271+
1272+ buf = buf / (periods.size ());
1273+
1274+ return buf;
1275+}
1276+
1277+/* Returns false if a value exists outside
1278+ * the threshold */
1279+bool
1280+VBlankWaiter::checkTimings (float req, float threshold)
1281+{
1282+ std::sort (periods.begin (), periods.end ());
1283+
1284+ float median = periods[periods.size () / 2];
1285+ std::vector <float> deviationPeriods (periods.size ());
1286+
1287+ unsigned int i = 0;
1288+
1289+ for (std::vector <float>::iterator it = periods.begin ();
1290+ it != periods.end (); it++, i++)
1291+ deviationPeriods[i] = fabsf ((*it) - median);
1292+
1293+ std::sort (deviationPeriods.begin (), deviationPeriods.end ());
1294+
1295+ float medianDeviation = deviationPeriods[deviationPeriods.size () / 2];
1296+
1297+ std::cout << "DEBUG: median absolute deviation of the periods was " << medianDeviation << std::endl;
1298+
1299+ if (medianDeviation > threshold)
1300+ return false;
1301+
1302+ /* No phase checks if there is no vblanking */
1303+ if (hasVSync ())
1304+ {
1305+
1306+ /* The phase needs to be sensitive to outliers
1307+ * so use the standard deviation with an acceptable
1308+ * deviation of 1 */
1309+
1310+ std::vector <VBlankWaiter::time_value>::iterator pit = paintPeriods.begin ();
1311+ std::vector <VBlankWaiter::time_value>::iterator vit = blankPeriods.begin ();
1312+ float sum = 0.0f;
1313+ float mean = 0.0f;
1314+
1315+ /* Check if any blank periods fall outside of -4ms
1316+ * of the paint period, that means we're out of phase. */
1317+
1318+ for (; vit != blankPeriods.end (); vit++, pit++)
1319+ sum += ((pit->tv_usec - vit->tv_usec) / 1000.0f);
1320+
1321+ mean = sum / blankPeriods.size ();
1322+
1323+ pit = paintPeriods.begin ();
1324+ vit = blankPeriods.begin ();
1325+
1326+ sum = 0.0f;
1327+
1328+ for (; vit != blankPeriods.end (); vit++, pit++)
1329+ sum += pow (((pit->tv_usec - vit->tv_usec) / 1000.0f) - mean, 2);
1330+
1331+ sum /= blankPeriods.size ();
1332+
1333+ std::cout << "DEBUG: st. dev of the phases was " << sqrt (sum) << std::endl;
1334+
1335+ if (sqrt (sum) > 0.025f)
1336+ return false;
1337+ }
1338+
1339+ return true;
1340+}
1341+
1342+bool
1343+VBlankWaiter::addPaintStart (unsigned int time)
1344+{
1345+ if (timingCount < periods.size ())
1346+ periods[timingCount]= time;
1347+ else
1348+ return false;
1349+
1350+ ++timingCount;
1351+
1352+ return true;
1353+}
1354+
1355+void
1356+VBlankWaiter::addPaintDone (const VBlankWaiter::time_value &tv)
1357+{
1358+ if (paintCount < paintPeriods.size ())
1359+ paintPeriods[paintCount] = tv;
1360+ ++paintCount;
1361+}
1362+
1363+void
1364+VBlankWaiter::addVBlank (const VBlankWaiter::time_value &tv)
1365+{
1366+ /* There may be multiple vblanks per paint
1367+ * because of throttling, so use the last slot
1368+ * available for the paint period */
1369+ if (paintCount < blankPeriods.size ())
1370+ blankPeriods[paintCount] = tv;
1371+}
1372+
1373+VBlankWaiter::VBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
1374+ periods (n),
1375+ paintPeriods (n),
1376+ blankPeriods (n),
1377+ timingCount (0),
1378+ paintCount (0),
1379+ owner (o)
1380+{
1381+}
1382+
1383+VBlankWaiter::~VBlankWaiter ()
1384+{
1385+}
1386+
1387+SleepVBlankWaiter::SleepVBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
1388+ VBlankWaiter (o, n)
1389+{
1390+}
1391+
1392+SleepVBlankWaiter::~SleepVBlankWaiter ()
1393+{
1394+}
1395+
1396+bool
1397+SleepVBlankWaiter::waitVBlank ()
1398+{
1399+ /* Wait until the next interval of 16ms */
1400+ VBlankWaiter::time_value tv;
1401+ gettimeofday (&tv, NULL);
1402+
1403+ unsigned long usecIntervalWait = 16000 - (tv.tv_usec % 16000);
1404+
1405+ struct timespec slp;
1406+
1407+ slp.tv_sec = 0;
1408+ slp.tv_nsec = usecIntervalWait * 1000;
1409+
1410+ nanosleep (&slp, NULL);
1411+
1412+ gettimeofday (&tv, NULL);
1413+
1414+ addVBlank (tv);
1415+
1416+ return timingCount < periods.size ();
1417+}
1418+
1419+void
1420+DRMVBlankWaiter::vblankHandler (int fd, unsigned int frame, unsigned int sec, unsigned int usc, void *data)
1421+{
1422+ drmVBlank vbl;
1423+ DRMInfo *info = static_cast <DRMInfo *> (data);
1424+
1425+ setDRMVBlankType (&vbl.request.type, DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
1426+ vbl.request.sequence = 1;
1427+ vbl.request.signal = (unsigned long) data;
1428+
1429+ drmWaitVBlank (fd, &vbl);
1430+
1431+ g_mutex_lock (&info->self->eventMutex);
1432+
1433+ if (info->self->timingCount != info->self->periods.size ())
1434+ info->self->pendingVBlank = true;
1435+
1436+ g_cond_signal (&info->self->eventCondition);
1437+ g_mutex_unlock (&info->self->eventMutex);
1438+}
1439+
1440+gpointer
1441+DRMVBlankWaiter::drmEventHandlerThread (gpointer data)
1442+{
1443+ DRMVBlankWaiter *self = static_cast <DRMVBlankWaiter *> (data);
1444+
1445+ while (!self->owner->isDone ())
1446+ {
1447+ int ret;
1448+ struct pollfd pfd;
1449+
1450+ pfd.fd = self->drmFD;
1451+ pfd.events = POLLIN | POLLHUP;
1452+ pfd.revents = 0;
1453+
1454+ ret = poll (&pfd, 1, -1);
1455+
1456+ if (ret <= 0)
1457+ {
1458+ std::cout << "DEBUG: poll () failed" << std::endl;
1459+ perror ("poll");
1460+ return NULL;
1461+ }
1462+
1463+ ret = drmHandleEvent (self->drmFD, &self->evctx);
1464+ if (ret != 0)
1465+ {
1466+ drmError (ret, "DRMVBlankWaiter::setupEventContextThread");
1467+ return NULL;
1468+ }
1469+ }
1470+
1471+ return NULL;
1472+}
1473+
1474+DRMVBlankWaiter::DRMVBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
1475+ VBlankWaiter (o, n),
1476+ info (new DRMInfo (this)),
1477+ pendingVBlank (false)
1478+{
1479+ const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx" };
1480+
1481+ g_mutex_init (&eventMutex);
1482+ g_cond_init (&eventCondition);
1483+
1484+ for (unsigned int i = 0; i < ARRAY_SIZE (modules); i++)
1485+ {
1486+ std::cout << "DEBUG: attempting to load platform " << modules[i];
1487+ drmFD = drmOpen (modules[i], NULL);
1488+
1489+ if (drmFD < 0)
1490+ std::cout << " ... failed" << std::endl;
1491+ else
1492+ {
1493+ std::cout << " ... success" << std::endl;;
1494+ break;
1495+ }
1496+ }
1497+
1498+ if (drmFD < 0)
1499+ throw std::exception ();
1500+
1501+ memset (&mVbl, 0, sizeof (drmVBlank));
1502+ mVbl.request.type = DRM_VBLANK_RELATIVE;
1503+ mVbl.request.sequence = 0;
1504+
1505+ int ret = drmWaitVBlank (drmFD, &mVbl);
1506+
1507+ if (ret != 0)
1508+ {
1509+ drmError (ret, "DRMVBlankWaiter::DRMVBlankWaiter (RELATIVE)");
1510+ throw std::exception ();
1511+ }
1512+
1513+ setDRMVBlankType (&mVbl.request.type, DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
1514+ mVbl.request.sequence = 1;
1515+ mVbl.request.signal = (unsigned long) info;
1516+
1517+ ret = drmWaitVBlank (drmFD, &mVbl);
1518+
1519+ if (ret != 0)
1520+ {
1521+ drmError (ret, "DRMVBlankWaiter::DRMVBlankWaiter (RELATIVE EVENT)");
1522+ throw std::exception ();
1523+ }
1524+
1525+ memset (&evctx, 0, sizeof (drmEventContext));
1526+ evctx.version = DRM_EVENT_CONTEXT_VERSION;
1527+ evctx.vblank_handler = DRMVBlankWaiter::vblankHandler;
1528+ evctx.page_flip_handler = NULL;
1529+
1530+ eventThread = g_thread_try_new ("drm_wait_t", &DRMVBlankWaiter::drmEventHandlerThread, (void *) this, NULL);
1531+
1532+ if (!eventThread)
1533+ {
1534+ std::cout << "DEBUG: could not create thread" << std::endl;
1535+ throw std::exception ();
1536+ }
1537+}
1538+
1539+DRMVBlankWaiter::~DRMVBlankWaiter ()
1540+{
1541+ g_thread_join (eventThread);
1542+
1543+ delete info;
1544+
1545+ drmClose (drmFD);
1546+}
1547+
1548+NilVBlankWaiter::NilVBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
1549+ VBlankWaiter (o, n)
1550+{
1551+}
1552+
1553+bool
1554+NilVBlankWaiter::waitVBlank ()
1555+{
1556+ /* Just add a new "vblank" */
1557+ VBlankWaiter::time_value tv;
1558+ gettimeofday (&tv, NULL);
1559+
1560+ addVBlank (tv);
1561+
1562+ return timingCount < periods.size ();
1563+}
1564+
1565+
1566+NilVBlankWaiter::~NilVBlankWaiter ()
1567+{
1568+}
1569+
1570+bool
1571+DRMVBlankWaiter::hasVSync ()
1572+{
1573+ return true;
1574+}
1575+
1576+bool
1577+DRMVBlankWaiter::waitVBlank ()
1578+{
1579+ VBlankWaiter::time_value tv;
1580+
1581+ g_mutex_lock (&eventMutex);
1582+ if (!pendingVBlank && !owner->isDone ())
1583+ g_cond_wait (&eventCondition, &eventMutex);
1584+
1585+ pendingVBlank = false;
1586+ g_mutex_unlock (&eventMutex);
1587+
1588+ gettimeofday (&tv, NULL);
1589+
1590+ addVBlank (tv);
1591+
1592+ return true;
1593+}
1594+
1595+DummyPaintDispatch::DummyPaintDispatch (Glib::RefPtr <Glib::MainLoop> mainloop) :
1596+ sched (this),
1597+ waiter (NULL),
1598+ success (true),
1599+ ml (mainloop)
1600+{
1601+ sched.schedule ();
1602+}
1603+
1604+bool doTest (const std::string &testName, int refreshRate, float workFactor, bool vsync)
1605+{
1606+ TimeoutHandler *th = new TimeoutHandler ();
1607+ TimeoutHandler::SetDefault (th);
1608+
1609+ Glib::RefPtr <Glib::MainContext> ctx = Glib::MainContext::get_default ();
1610+ Glib::RefPtr <Glib::MainLoop> mainloop = Glib::MainLoop::create (ctx, false);
1611+ Glib::RefPtr <CompTimeoutSource> timeout = CompTimeoutSource::create (ctx);
1612+
1613+ DummyPaintDispatch *dpb = new DummyPaintDispatch (mainloop);
1614+ VBlankWaiter *vbwaiter = NULL;
1615+
1616+ if (vsync)
1617+ {
1618+ try
1619+ {
1620+ vbwaiter = new DRMVBlankWaiter (dpb, 200);
1621+ }
1622+ catch (std::exception &e)
1623+ {
1624+ std::cout << "WARN: can't test DRM vblanking! Using hardcoded nanosleep () based waiter" << std::endl;
1625+ vbwaiter = new SleepVBlankWaiter (dpb, 200);
1626+ }
1627+ }
1628+ else
1629+ vbwaiter = new NilVBlankWaiter (dpb, 200);
1630+
1631+ dpb->setWaiter (vbwaiter);
1632+ dpb->setRefreshRate (refreshRate);
1633+
1634+ Worker::setWorkFactor ((1000 / refreshRate) * workFactor);
1635+
1636+ std::cout << "INFO: " << testName << " with refresh rate of " << refreshRate << "Hz and work factor of " << workFactor << (vsync ? " with vertical sync" : " without vertical sync") << std::endl;
1637+
1638+ mainloop->run ();
1639+
1640+ float averagePeriod = vbwaiter->averagePeriod ();
1641+
1642+ std::cout << "DEBUG: average vblank wait time was " << averagePeriod << std::endl;
1643+ std::cout << "DEBUG: average frame rate " << 1000 / averagePeriod << " Hz" << std::endl;
1644+ std::cout << "TEST: " << testName << " time " << averagePeriod << " within threshold of " << 10.0f << std::endl;
1645+
1646+ if (vbwaiter->checkTimings (averagePeriod, 10.0f) &&
1647+ 1000 / averagePeriod < refreshRate)
1648+ pass (testName);
1649+ else
1650+ fail (testName);
1651+
1652+ dpb->setWaiter (NULL);
1653+
1654+ delete dpb;
1655+ delete th;
1656+
1657+ return true;
1658+}
1659+
1660+int main (void)
1661+{
1662+ gettimeofday (&SleepVBlankWaiter::start_time, NULL);
1663+
1664+ doTest ("unthrottled vblank timings", 60, 0.0f, true);
1665+ doTest ("no vsync 60 Hz refresh rate", 60, 0.0f, false);
1666+ doTest ("unthrottled vblank timings", 60, 0.8f, true);
1667+ doTest ("no vsync 60 Hz refresh rate", 60, 0.8f, false);
1668+ doTest ("vsync 50 Hz refresh rate", 50, 0.0f, true);
1669+ doTest ("no vsync 20 Hz refresh rate", 30, 0.0f,false);
1670+ doTest ("vsync 50 Hz refresh rate", 50, 1.6f, true);
1671+ doTest ("no vsync 20 Hz refresh rate", 30, 1.6f,false);
1672+ doTest ("vsync 30 Hz refresh rate", 30, 0.0f, true);
1673+ doTest ("no vsync 100 Hz refresh rate", 100, 0.0f, false);
1674+ doTest ("vsync 100 Hz refresh rate", 100, 0.0f, true);
1675+ doTest ("no vsync 100 Hz refresh rate", 100, 2.8f, false);
1676+ doTest ("vsync 100 Hz refresh rate", 100, 2.8f, true);
1677+
1678+ return 0;
1679+}
1680
1681=== modified file 'plugins/opengl/include/opengl/opengl.h'
1682--- plugins/opengl/include/opengl/opengl.h 2012-01-12 06:48:58 +0000
1683+++ plugins/opengl/include/opengl/opengl.h 2012-01-20 06:45:01 +0000
1684@@ -102,6 +102,8 @@
1685 typedef int (*GLXWaitVideoSyncProc) (int divisor,
1686 int remainder,
1687 unsigned int *count);
1688+ typedef int (*GLXSwapIntervalProc) (int interval);
1689+
1690
1691 #ifndef GLX_VERSION_1_3
1692 typedef struct __GLXFBConfigRec *GLXFBConfig;
1693@@ -165,6 +167,7 @@
1694 extern GLXCopySubBufferProc copySubBuffer;
1695 extern GLXGetVideoSyncProc getVideoSync;
1696 extern GLXWaitVideoSyncProc waitVideoSync;
1697+ extern GLXSwapIntervalProc swapInterval;
1698 extern GLXGetFBConfigsProc getFBConfigs;
1699 extern GLXGetFBConfigAttribProc getFBConfigAttrib;
1700 extern GLXCreatePixmapProc createPixmap;
1701
1702=== modified file 'plugins/opengl/src/privates.h'
1703--- plugins/opengl/src/privates.h 2012-01-12 06:48:58 +0000
1704+++ plugins/opengl/src/privates.h 2012-01-20 06:45:01 +0000
1705@@ -67,6 +67,9 @@
1706 const CompRegion &region);
1707
1708 bool hasVSync ();
1709+ bool waitVSync (unsigned int mask);
1710+
1711+ void syncBuffers (unsigned int mask);
1712
1713 void prepareDrawing ();
1714
1715@@ -115,6 +118,7 @@
1716 GLXContext ctx;
1717
1718 CompRegion outputRegion;
1719+ CompRegion tmpRegion;
1720
1721 bool pendingCommands;
1722
1723
1724=== modified file 'plugins/opengl/src/screen.cpp'
1725--- plugins/opengl/src/screen.cpp 2012-01-12 06:48:58 +0000
1726+++ plugins/opengl/src/screen.cpp 2012-01-20 06:45:01 +0000
1727@@ -31,15 +31,13 @@
1728 #include <math.h>
1729
1730 namespace GL {
1731- typedef int (*GLXSwapIntervalProc) (int interval);
1732-
1733- GLXSwapIntervalProc swapInterval = NULL;
1734 GLXBindTexImageProc bindTexImage = NULL;
1735 GLXReleaseTexImageProc releaseTexImage = NULL;
1736 GLXQueryDrawableProc queryDrawable = NULL;
1737 GLXCopySubBufferProc copySubBuffer = NULL;
1738 GLXGetVideoSyncProc getVideoSync = NULL;
1739 GLXWaitVideoSyncProc waitVideoSync = NULL;
1740+ GLXSwapIntervalProc swapInterval = NULL;
1741 GLXGetFBConfigsProc getFBConfigs = NULL;
1742 GLXGetFBConfigAttribProc getFBConfigAttrib = NULL;
1743 GLXCreatePixmapProc createPixmap = NULL;
1744@@ -1081,6 +1079,7 @@
1745 waitForVideoSync ()
1746 {
1747 GL::unthrottledFrames++;
1748+ // Docs: http://www.opengl.org/registry/specs/SGI/video_sync.txt
1749 if (GL::waitVideoSync)
1750 {
1751 // Don't wait twice. Just in case.
1752@@ -1122,13 +1121,6 @@
1753 } // namespace GL
1754
1755 void
1756-PrivateGLScreen::waitForVideoSync ()
1757-{
1758- if (optionGetSyncToVblank ())
1759- GL::waitForVideoSync ();
1760-}
1761-
1762-void
1763 PrivateGLScreen::paintOutputs (CompOutput::ptrList &outputs,
1764 unsigned int mask,
1765 const CompRegion &region)
1766@@ -1141,7 +1133,7 @@
1767 glClear (GL_COLOR_BUFFER_BIT);
1768 }
1769
1770- CompRegion tmpRegion (region);
1771+ tmpRegion = region;
1772
1773 foreach (CompOutput *output, outputs)
1774 {
1775@@ -1196,7 +1188,11 @@
1776 }
1777
1778 targetOutput = &screen->outputDevs ()[0];
1779+}
1780
1781+bool
1782+PrivateGLScreen::waitVSync (unsigned int mask)
1783+{
1784 if (mask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
1785 {
1786 /*
1787@@ -1205,16 +1201,27 @@
1788 * Unfortunately it only works with glXSwapBuffers in most drivers.
1789 */
1790 GL::controlSwapVideoSync (optionGetSyncToVblank ());
1791+ return false;
1792+ }
1793+ else
1794+ {
1795+ GL::waitForVideoSync ();
1796+ return true;
1797+ }
1798+}
1799+
1800+void
1801+PrivateGLScreen::syncBuffers (unsigned int mask)
1802+{
1803+ if (mask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
1804+ {
1805 glXSwapBuffers (screen->dpy (), cScreen->output ());
1806 }
1807 else
1808 {
1809- BoxPtr pBox;
1810- int nBox, y;
1811-
1812- waitForVideoSync ();
1813- pBox = const_cast <Region> (tmpRegion.handle ())->rects;
1814- nBox = const_cast <Region> (tmpRegion.handle ())->numRects;
1815+ BoxPtr pBox = const_cast <Region> (tmpRegion.handle ())->rects;
1816+ int nBox = const_cast <Region> (tmpRegion.handle ())->numRects;
1817+ int y;
1818
1819 if (GL::copySubBuffer)
1820 {

Subscribers

People subscribed via source and target branches

to all changes: