Merge lp:~smspillaz/compiz-core/compiz-core.fix-880707-test-v3 into lp:compiz-core/0.9.5

Proposed by Sam Spilsbury
Status: Merged
Merged at revision: 2938
Proposed branch: lp:~smspillaz/compiz-core/compiz-core.fix-880707-test-v3
Merge into: lp:compiz-core/0.9.5
Diff against target: 2040 lines (+1320/-294)
20 files modified
cmake/CompizPlugin.cmake (+8/-4)
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 (+96/-0)
plugins/composite/src/privates.h (+19/-13)
plugins/composite/src/screen.cpp (+120/-196)
plugins/composite/tests/CMakeLists.txt (+1/-0)
plugins/composite/tests/paintscheduler/CMakeLists.txt (+36/-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)
src/CMakeLists.txt (+10/-2)
src/screen.cpp (+48/-11)
To merge this branch: bzr merge lp:~smspillaz/compiz-core/compiz-core.fix-880707-test-v3
Reviewer Review Type Date Requested Status
Alan Griffiths Abstain
Daniel van Vugt Pending
Tim Penhey Pending
Review via email: mp+89402@code.launchpad.net

This proposal supersedes 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

>
> 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 : Posted in a previous version of this proposal

(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 : Posted in a previous version of this proposal

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

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

> >
> > 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.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

> 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 : Posted in a previous version of this proposal

> 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

> 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal
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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

Will have another look at this tomorrow.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

(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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

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

Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

Hey Daniel,

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

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Conflicts fixed.

Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

The target branch is now "finished". Please proposed for merging into lp:compiz-core instead.

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

Whoops. Fixed.

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

(A note that I will change this to use google test and the testing infrastructure in compiz once this is merged)

2919. By Sam Spilsbury

Merged from lp:~thomas-voss/compiz-core/fix-bad-get-and-missing-symbols

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I really can't tell if the code has the intended effect, but it largely looks reasonable.

I presume from the comment trail that vanvugh will verify?

BTW the ARRAY_SIZE is better written as a simple template:

template<typename Type, size_t N>
inline size_t length_of(Type (&)[N]) { return N; }

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

It appears this got merged without anyone fixing the build failures :(

Unfortunate, because I had planned on reviewing this proposal again this week.

I'll try to fix the errors as part of bug 921406.

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

Subscribers

People subscribed via source and target branches