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
=== modified file 'cmake/CompizPlugin.cmake'
--- cmake/CompizPlugin.cmake 2012-01-12 06:48:58 +0000
+++ cmake/CompizPlugin.cmake 2012-01-20 14:46:26 +0000
@@ -342,7 +342,7 @@
342 ${${_PLUGIN}_PKG_INCDIRS}342 ${${_PLUGIN}_PKG_INCDIRS}
343 ${${_PLUGIN}_INCDIRS}343 ${${_PLUGIN}_INCDIRS}
344 ${COMPIZ_INCLUDE_DIRS}344 ${COMPIZ_INCLUDE_DIRS}
345 ${CMAKE_PREFIX_PATH}/include345 ${PLUGIN_PREFIX}/include
346 ${CMAKE_INCLUDE_PATH}346 ${CMAKE_INCLUDE_PATH}
347 ${${_PLUGIN}_MOD_INCLUDE_DIRS}347 ${${_PLUGIN}_MOD_INCLUDE_DIRS}
348 ${CORE_MOD_INCLUDE_DIRS}348 ${CORE_MOD_INCLUDE_DIRS}
@@ -353,16 +353,20 @@
353 GLOBAL353 GLOBAL
354 PROPERTY ${_PLUGIN}_MOD_LIBRARY_DIRS)354 PROPERTY ${_PLUGIN}_MOD_LIBRARY_DIRS)
355355
356 set (SYSTEM_LINK_DIRS
357 "/usr/lib"
358 "/usr/lib32"
359 "/usr/lib64"
360 "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
361
356 link_directories (362 link_directories (
357 ${COMPIZ_LINK_DIRS}363 ${COMPIZ_LINK_DIRS}
358 ${${_PLUGIN}_PKG_LIBDIRS}364 ${${_PLUGIN}_PKG_LIBDIRS}
359 ${${_PLUGIN}_LIBDIRS}365 ${${_PLUGIN}_LIBDIRS}
360 ${PLUGIN_LIBDIR}366 ${PLUGIN_LIBDIR}
361 ${COMPIZ_LIBDIR}/compiz367 ${COMPIZ_LIBDIR}/compiz
362 ${CMAKE_PREFIX_PATH}/lib
363 ${CMAKE_PREFIX_PATH}/lib32
364 ${CMAKE_PREFIX_PATH}/lib64
365 ${${_PLUGIN}_MOD_LIBRARY_DIRS}368 ${${_PLUGIN}_MOD_LIBRARY_DIRS}
369 ${SYSTEM_LINK_DIRS}
366 )370 )
367371
368 add_library (372 add_library (
369373
=== modified file 'plugins/composite/CMakeLists.txt'
--- plugins/composite/CMakeLists.txt 2011-03-11 12:15:30 +0000
+++ plugins/composite/CMakeLists.txt 2012-01-20 14:46:26 +0000
@@ -3,3 +3,5 @@
3include (CompizPlugin)3include (CompizPlugin)
44
5compiz_plugin (composite)5compiz_plugin (composite)
6
7add_subdirectory (tests)
68
=== modified file 'plugins/composite/composite.xml.in'
--- plugins/composite/composite.xml.in 2009-08-08 06:00:14 +0000
+++ plugins/composite/composite.xml.in 2012-01-20 14:46:26 +0000
@@ -8,16 +8,11 @@
8 <_short>Slow Animations</_short>8 <_short>Slow Animations</_short>
9 <_long>Toggle use of slow animations</_long>9 <_long>Toggle use of slow animations</_long>
10 </option>10 </option>
11 <option name="detect_refresh_rate" type="bool">11 <option name="refresh_rate_cap" type="int">
12 <_short>Detect Refresh Rate</_short>12 <_short>Refresh Rate Cap</_short>
13 <_long>Automatic detection of refresh rate</_long>13 <_long>Maximum allowed refresh rate (set -1 to force VSync usage)</_long>
14 <default>true</default>14 <default>200</default>
15 </option>15 <min>-1</min>
16 <option name="refresh_rate" type="int">
17 <_short>Refresh Rate</_short>
18 <_long>The rate at which the screen is redrawn (times/second)</_long>
19 <default>50</default>
20 <min>1</min>
21 <max>200</max>16 <max>200</max>
22 </option>17 </option>
23 <option name="unredirect_fullscreen_windows" type="bool">18 <option name="unredirect_fullscreen_windows" type="bool">
2419
=== modified file 'plugins/composite/include/composite/composite.h'
--- plugins/composite/include/composite/composite.h 2012-01-18 16:26:45 +0000
+++ plugins/composite/include/composite/composite.h 2012-01-20 14:46:26 +0000
@@ -37,6 +37,7 @@
37#include "core/output.h"37#include "core/output.h"
38#include "core/screen.h"38#include "core/screen.h"
39#include "core/wrapsystem.h"39#include "core/wrapsystem.h"
40#include "composite/fpslimiter.h"
4041
41#define COMPOSITE_SCREEN_DAMAGE_PENDING_MASK (1 << 0)42#define COMPOSITE_SCREEN_DAMAGE_PENDING_MASK (1 << 0)
42#define COMPOSITE_SCREEN_DAMAGE_REGION_MASK (1 << 1)43#define COMPOSITE_SCREEN_DAMAGE_REGION_MASK (1 << 1)
@@ -80,14 +81,6 @@
80 */81 */
81#define PAINT_SCREEN_NO_BACKGROUND_MASK (1 << 6)82#define PAINT_SCREEN_NO_BACKGROUND_MASK (1 << 6)
8283
83
84typedef enum
85{
86 CompositeFPSLimiterModeDisabled = 0,
87 CompositeFPSLimiterModeDefault,
88 CompositeFPSLimiterModeVSyncLike
89} CompositeFPSLimiterMode;
90
91class PrivateCompositeScreen;84class PrivateCompositeScreen;
92class PrivateCompositeWindow;85class PrivateCompositeWindow;
93class CompositeScreen;86class CompositeScreen;
@@ -106,6 +99,10 @@
106 const CompRegion &region) = 0;99 const CompRegion &region) = 0;
107100
108 virtual bool hasVSync () { return false; };101 virtual bool hasVSync () { return false; };
102 /* Return true if we are allowed to wait some more
103 * for framerate throttling */
104 virtual bool waitVSync (unsigned int mask) { return false; }
105 virtual void syncBuffers (unsigned int mask) = 0;
109106
110 virtual void prepareDrawing () {};107 virtual void prepareDrawing () {};
111 virtual bool compositingActive () { return false; };108 virtual bool compositingActive () { return false; };
@@ -173,10 +170,6 @@
173 public CompOption::Class170 public CompOption::Class
174{171{
175 public:172 public:
176
177
178
179 public:
180 CompositeScreen (CompScreen *s);173 CompositeScreen (CompScreen *s);
181 ~CompositeScreen ();174 ~CompositeScreen ();
182175
183176
=== added file 'plugins/composite/include/composite/fpslimiter.h'
--- plugins/composite/include/composite/fpslimiter.h 1970-01-01 00:00:00 +0000
+++ plugins/composite/include/composite/fpslimiter.h 2012-01-20 14:46:26 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright © 2008 Dennis Kasprzyk
3 * Copyright © 2007 Novell, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of
10 * Dennis Kasprzyk not be used in advertising or publicity pertaining to
11 * distribution of the software without specific, written prior permission.
12 * Dennis Kasprzyk makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
14 * implied warranty.
15 *
16 * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18 * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
22 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
25 * David Reveman <davidr@novell.com>
26 */
27
28#ifndef _COMPIZ_COMPOSITE_FPSLIMITER_H
29#define _COMPIZ_COMPOSITE_FPSLIMITER_H
30
31typedef enum
32{
33 CompositeFPSLimiterModeDisabled = 0,
34 CompositeFPSLimiterModeDefault,
35 CompositeFPSLimiterModeVSyncLike
36} CompositeFPSLimiterMode;
37
38#endif
039
=== modified file 'plugins/composite/src/composite.cpp'
--- plugins/composite/src/composite.cpp 2012-01-18 16:26:45 +0000
+++ plugins/composite/src/composite.cpp 2012-01-20 14:46:26 +0000
@@ -57,35 +57,6 @@
57}57}
5858
59bool59bool
60PrivateCompositeScreen::setOption (const CompString &name,
61 CompOption::Value &value)
62{
63 unsigned int index;
64
65 bool rv = CompositeOptions::setOption (name, value);
66
67 if (!rv || !CompOption::findOption (getOptions (), name, &index))
68 return false;
69
70 switch (index) {
71 case CompositeOptions::DetectRefreshRate:
72 if (optionGetDetectRefreshRate ())
73 detectRefreshRate ();
74 break;
75 case CompositeOptions::RefreshRate:
76 if (optionGetDetectRefreshRate ())
77 return false;
78 redrawTime = 1000 / optionGetRefreshRate ();
79 optimalRedrawTime = redrawTime;
80 break;
81 default:
82 break;
83 }
84
85 return rv;
86}
87
88bool
89CompositePluginVTable::init ()60CompositePluginVTable::init ()
90{61{
91 if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))62 if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
9263
=== added file 'plugins/composite/src/paintscheduler.cpp'
--- plugins/composite/src/paintscheduler.cpp 1970-01-01 00:00:00 +0000
+++ plugins/composite/src/paintscheduler.cpp 2012-01-20 14:46:26 +0000
@@ -0,0 +1,189 @@
1/*
2 * Copyright © 2008 Dennis Kasprzyk
3 * Copyright © 2007 Novell, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of
10 * Dennis Kasprzyk not be used in advertising or publicity pertaining to
11 * distribution of the software without specific, written prior permission.
12 * Dennis Kasprzyk makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
14 * implied warranty.
15 *
16 * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18 * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
22 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
25 * David Reveman <davidr@novell.com>
26 * Daniel van Vugt <vanvugt@gmail.com>
27 * Sam Spilsbury <sam.spilsbury@canonical.com>
28 */
29
30#include "paintscheduler.h"
31#include <boost/bind.hpp>
32
33compiz::composite::scheduler::PaintScheduler::PaintScheduler (PaintSchedulerDispatchBase *b) :
34 mSchedulerState (0),
35 mRedrawTime (1000 / 50),
36 mOptimalRedrawTime (1000 / 50),
37 mFPSLimiterMode (CompositeFPSLimiterModeDefault),
38 mDispatchBase (b)
39{
40 gettimeofday (&mLastRedraw, 0);
41}
42
43compiz::composite::scheduler::PaintScheduler::~PaintScheduler ()
44{
45 mPaintTimer.stop ();
46}
47
48bool
49compiz::composite::scheduler::PaintScheduler::schedule ()
50{
51 int delay = 1;
52
53 if (!mDispatchBase->schedulerCompositingActive ())
54 return false;
55
56 if (mSchedulerState & paintSchedulerPainting)
57 {
58 mSchedulerState |= paintSchedulerReschedule;
59 return false;
60 }
61
62 if (mSchedulerState & paintSchedulerScheduled)
63 return false;
64
65 mSchedulerState |= paintSchedulerScheduled;
66
67 if (mFPSLimiterMode == CompositeFPSLimiterModeVSyncLike ||
68 (mDispatchBase->schedulerHasVsync ()))
69 {
70 delay = 1;
71 }
72 else
73 {
74 struct timeval now;
75 gettimeofday (&now, 0);
76 int elapsed = compiz::core::timer::timeval_diff (&now, &mLastRedraw);
77 if (elapsed < 0)
78 elapsed = 0;
79 delay = elapsed < mOptimalRedrawTime ? mOptimalRedrawTime - elapsed + 1 : 1;
80 }
81 /*
82 * Note the use of delay = 1 instead of 0, even though 0 would be better.
83 * A delay of zero is presently broken due to CompTimer bugs;
84 * 1. Infinite loop in CompTimeoutSource::callback when a zero
85 * timer is set.
86 * 2. Priority set too high in CompTimeoutSource::CompTimeoutSource
87 * causing the glib main event loop to be starved of X events.
88 * Fixes for both of these issues are being worked on separately.
89 */
90
91 mPaintTimer.start
92 (boost::bind (&compiz::composite::scheduler::PaintScheduler::dispatch, this),
93 delay);
94
95 return false;
96}
97
98int
99compiz::composite::scheduler::PaintScheduler::getRedrawTime ()
100{
101 return mRedrawTime;
102}
103
104int
105compiz::composite::scheduler::PaintScheduler::getOptimalRedrawTime ()
106{
107 return mOptimalRedrawTime;
108}
109
110bool
111compiz::composite::scheduler::PaintScheduler::dispatch ()
112{
113 struct timeval tv;
114 int timeDiff;
115
116 gettimeofday (&tv, 0);
117
118 timeDiff = compiz::core::timer::timeval_diff (&tv, &mLastRedraw);
119
120 /* handle clock rollback */
121
122 if (timeDiff < 0)
123 timeDiff = 0;
124
125 /*
126 * Now that we use a "tickless" timing algorithm, timeDiff could be
127 * very large if the screen is truely idle.
128 * However plugins expect the old behaviour where timeDiff is never
129 * larger than the frame rate (optimalRedrawTime).
130 * So enforce this to keep animations timed correctly and smooth...
131 */
132
133 if (timeDiff > mOptimalRedrawTime &&
134 !(mSchedulerState & paintSchedulerReschedule))
135 timeDiff = mOptimalRedrawTime;
136
137 mSchedulerState |= paintSchedulerPainting;
138 mSchedulerState &= ~paintSchedulerReschedule;
139
140 mRedrawTime = timeDiff;
141
142 mDispatchBase->prepareScheduledPaint (timeDiff);
143 mDispatchBase->paintScheduledPaint ();
144
145 mLastRedraw = tv;
146
147 /* Throttle to the allowed refresh rate */
148 if (mFPSLimiterMode == CompositeFPSLimiterModeDefault)
149 {
150 if (mDispatchBase->schedulerHasVsync ())
151 {
152 do
153 {
154 /* If throttling is allowed, ensure that
155 * timeDiff is always >= mOptimalRedrawTime
156 * by waiting for another video-sync */
157 if (mDispatchBase->syncScheduledPaint ())
158 {
159 struct timeval stv;
160 gettimeofday (&stv, 0);
161 timeDiff = compiz::core::timer::timeval_diff (&stv, &mLastRedraw);
162 }
163 else
164 timeDiff = mOptimalRedrawTime;
165 }
166 while (timeDiff < mOptimalRedrawTime);
167 }
168 }
169 else if (mFPSLimiterMode == CompositeFPSLimiterModeVSyncLike)
170 {
171 if (mDispatchBase->schedulerHasVsync ())
172 mDispatchBase->syncScheduledPaint ();
173 }
174
175 mDispatchBase->doneScheduledPaint ();
176 mSchedulerState &= ~(paintSchedulerPainting | paintSchedulerScheduled);
177
178 if (mSchedulerState & paintSchedulerReschedule)
179 schedule ();
180
181 return false;
182}
183
184void
185compiz::composite::scheduler::PaintScheduler::setRefreshRate (unsigned int rate)
186{
187 mOptimalRedrawTime = mRedrawTime =
188 static_cast <int> (1000 / static_cast <float> (rate));
189}
0190
=== added file 'plugins/composite/src/paintscheduler.h'
--- plugins/composite/src/paintscheduler.h 1970-01-01 00:00:00 +0000
+++ plugins/composite/src/paintscheduler.h 2012-01-20 14:46:26 +0000
@@ -0,0 +1,96 @@
1/*
2 * Copyright © 2008 Dennis Kasprzyk
3 * Copyright © 2007 Novell, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of
10 * Dennis Kasprzyk not be used in advertising or publicity pertaining to
11 * distribution of the software without specific, written prior permission.
12 * Dennis Kasprzyk makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
14 * implied warranty.
15 *
16 * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18 * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
22 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Authors: Dennis Kasprzyk <onestone@compiz-fusion.org>
25 * David Reveman <davidr@novell.com>
26 */
27
28#include <core/timer.h>
29#include <composite/fpslimiter.h>
30
31#ifndef _COMPIZ_COMPOSITE_PAINTSCHEDULER_H
32#define _COMPIZ_COMPOSITE_PAINTSCHEDULER_H
33
34namespace compiz
35{
36namespace composite
37{
38namespace scheduler
39{
40
41const unsigned int paintSchedulerScheduled = 1 << 0;
42const unsigned int paintSchedulerPainting = 1 << 1;
43const unsigned int paintSchedulerReschedule = 1 << 2;
44
45class PaintSchedulerDispatchBase
46{
47 public:
48
49 virtual void prepareScheduledPaint (unsigned int timeDiff) = 0;
50 virtual void paintScheduledPaint () = 0;
51 virtual bool syncScheduledPaint () = 0;
52 virtual void doneScheduledPaint () = 0;
53 virtual bool schedulerCompositingActive () = 0;
54 virtual bool schedulerHasVsync () = 0;
55};
56
57class PaintScheduler
58{
59 public:
60
61 PaintScheduler (PaintSchedulerDispatchBase *);
62 ~PaintScheduler ();
63
64 bool schedule ();
65 void setFPSLimiterMode (CompositeFPSLimiterMode mode);
66 CompositeFPSLimiterMode getFPSLimiterMode ();
67
68 int getRedrawTime ();
69 int getOptimalRedrawTime ();
70
71 void setRefreshRate (unsigned int);
72
73 protected:
74
75 bool dispatch ();
76
77 private:
78
79 unsigned int mSchedulerState;
80
81 struct timeval mLastRedraw;
82 int mRedrawTime;
83 int mOptimalRedrawTime;
84
85 CompositeFPSLimiterMode mFPSLimiterMode;
86
87 CompTimer mPaintTimer;
88
89 PaintSchedulerDispatchBase *mDispatchBase;
90};
91
92}
93}
94}
95
96#endif
097
=== modified file 'plugins/composite/src/privates.h'
--- plugins/composite/src/privates.h 2011-11-14 07:41:22 +0000
+++ plugins/composite/src/privates.h 2012-01-20 14:46:26 +0000
@@ -31,6 +31,8 @@
31#include <composite/composite.h>31#include <composite/composite.h>
32#include <core/atoms.h>32#include <core/atoms.h>
3333
34#include "paintscheduler.h"
35
34#include "composite_options.h"36#include "composite_options.h"
3537
36#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR > 238#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR > 2
@@ -41,17 +43,17 @@
41extern CompPlugin::VTable *compositeVTable;43extern CompPlugin::VTable *compositeVTable;
4244
43extern CompWindow *lastDamagedWindow;45extern CompWindow *lastDamagedWindow;
46
4447
45class PrivateCompositeScreen :48class PrivateCompositeScreen :
46 ScreenInterface,49 ScreenInterface,
50 public compiz::composite::scheduler::PaintSchedulerDispatchBase,
47 public CompositeOptions51 public CompositeOptions
48{52{
49 public:53 public:
50 PrivateCompositeScreen (CompositeScreen *cs);54 PrivateCompositeScreen (CompositeScreen *cs);
51 ~PrivateCompositeScreen ();55 ~PrivateCompositeScreen ();
5256
53 bool setOption (const CompString &name, CompOption::Value &value);
54
55 void outputChangeNotify ();57 void outputChangeNotify ();
5658
57 void handleEvent (XEvent *event);59 void handleEvent (XEvent *event);
@@ -64,7 +66,19 @@
6466
65 void detectRefreshRate ();67 void detectRefreshRate ();
6668
67 void scheduleRepaint ();69 protected:
70
71 void prepareScheduledPaint (unsigned int timeDiff);
72
73 void paintScheduledPaint ();
74
75 bool syncScheduledPaint ();
76
77 void doneScheduledPaint ();
78
79 bool schedulerCompositingActive ();
80
81 bool schedulerHasVsync ();
6882
69 public:83 public:
7084
@@ -94,18 +108,10 @@
94108
95 int overlayWindowCount;109 int overlayWindowCount;
96110
97 struct timeval lastRedraw;
98 int redrawTime;
99 int optimalRedrawTime;
100 bool scheduled, painting, reschedule;
101
102 bool slowAnimations;111 bool slowAnimations;
103112
104 CompTimer paintTimer;113 compiz::composite::PaintHandler *pHnd;
105114 compiz::composite::scheduler::PaintScheduler scheduler;
106 compiz::composite::PaintHandler *pHnd;
107
108 CompositeFPSLimiterMode FPSLimiterMode;
109115
110 CompWindowList withDestroyedWindows;116 CompWindowList withDestroyedWindows;
111};117};
112118
=== modified file 'plugins/composite/src/screen.cpp'
--- plugins/composite/src/screen.cpp 2012-01-19 04:07:41 +0000
+++ plugins/composite/src/screen.cpp 2012-01-20 14:46:26 +0000
@@ -175,16 +175,6 @@
175 }175 }
176 }176 }
177 }177 }
178 else if (randrExtension &&
179 event->type == randrEvent + RRScreenChangeNotify)
180 {
181 XRRScreenChangeNotifyEvent *rre;
182
183 rre = (XRRScreenChangeNotifyEvent *) event;
184
185 if (screen->root () == rre->root)
186 detectRefreshRate ();
187 }
188 break;178 break;
189 }179 }
190}180}
@@ -259,8 +249,6 @@
259249
260CompositeScreen::~CompositeScreen ()250CompositeScreen::~CompositeScreen ()
261{251{
262 priv->paintTimer.stop ();
263
264#ifdef USE_COW252#ifdef USE_COW
265 if (useCow)253 if (useCow)
266 XCompositeReleaseOverlayWindow (screen->dpy (),254 XCompositeReleaseOverlayWindow (screen->dpy (),
@@ -279,21 +267,17 @@
279 exposeRects (),267 exposeRects (),
280 windowPaintOffset (0, 0),268 windowPaintOffset (0, 0),
281 overlayWindowCount (0),269 overlayWindowCount (0),
282 redrawTime (1000 / 50),
283 optimalRedrawTime (1000 / 50),
284 scheduled (false),
285 painting (false),
286 reschedule (false),
287 slowAnimations (false),270 slowAnimations (false),
288 pHnd (NULL),271 pHnd (NULL),
289 FPSLimiterMode (CompositeFPSLimiterModeDefault),272 scheduler (this),
290 withDestroyedWindows ()273 withDestroyedWindows ()
291{274{
292 gettimeofday (&lastRedraw, 0);
293 // wrap outputChangeNotify275 // wrap outputChangeNotify
294 ScreenInterface::setHandler (screen);276 ScreenInterface::setHandler (screen);
295277
296 optionSetSlowAnimationsKeyInitiate (CompositeScreen::toggleSlowAnimations);278 optionSetSlowAnimationsKeyInitiate (CompositeScreen::toggleSlowAnimations);
279 optionSetRefreshRateCapNotify (boost::bind (&PrivateCompositeScreen::detectRefreshRate,
280 this));
297}281}
298282
299PrivateCompositeScreen::~PrivateCompositeScreen ()283PrivateCompositeScreen::~PrivateCompositeScreen ()
@@ -445,7 +429,6 @@
445 CompositeRedirectManual);429 CompositeRedirectManual);
446430
447 priv->pHnd = NULL;431 priv->pHnd = NULL;
448 priv->paintTimer.stop ();
449432
450 hideOutputWindow ();433 hideOutputWindow ();
451}434}
@@ -464,7 +447,7 @@
464{447{
465 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_ALL_MASK;448 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
466 priv->damageMask &= ~COMPOSITE_SCREEN_DAMAGE_REGION_MASK;449 priv->damageMask &= ~COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
467 priv->scheduleRepaint ();450 priv->scheduler.schedule ();
468}451}
469452
470void453void
@@ -483,14 +466,14 @@
483466
484 if (priv->damage.numRects () > 100)467 if (priv->damage.numRects () > 100)
485 damageScreen ();468 damageScreen ();
486 priv->scheduleRepaint ();469 priv->scheduler.schedule ();
487}470}
488471
489void472void
490CompositeScreen::damagePending ()473CompositeScreen::damagePending ()
491{474{
492 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_PENDING_MASK;475 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_PENDING_MASK;
493 priv->scheduleRepaint ();476 priv->scheduler.schedule ();
494}477}
495478
496unsigned int479unsigned int
@@ -635,204 +618,145 @@
635void618void
636PrivateCompositeScreen::detectRefreshRate ()619PrivateCompositeScreen::detectRefreshRate ()
637{620{
638 if (!noDetection &&621 unsigned int rate = optionGetRefreshRateCap ();
639 optionGetDetectRefreshRate ())622
640 {623 if (rate)
641 CompString name;624 scheduler.setRefreshRate (rate);
642 CompOption::Value value;
643
644 value.set ((int) 0);
645
646 if (screen->XRandr ())
647 {
648 XRRScreenConfiguration *config;
649
650 config = XRRGetScreenInfo (screen->dpy (),
651 screen->root ());
652 value.set ((int) XRRConfigCurrentRate (config));
653
654 XRRFreeScreenConfigInfo (config);
655 }
656
657 if (value.i () == 0)
658 value.set ((int) 50);
659
660 mOptions[CompositeOptions::DetectRefreshRate].value ().set (false);
661 screen->setOptionForPlugin ("composite", "refresh_rate", value);
662 mOptions[CompositeOptions::DetectRefreshRate].value ().set (true);
663 optimalRedrawTime = redrawTime = 1000 / value.i ();
664 }
665 else625 else
666 {626 scheduler.setFPSLimiterMode (CompositeFPSLimiterModeVSyncLike);
667 redrawTime = 1000 / optionGetRefreshRate ();
668 optimalRedrawTime = redrawTime;
669 }
670}627}
671628
672CompositeFPSLimiterMode629CompositeFPSLimiterMode
673CompositeScreen::FPSLimiterMode ()630CompositeScreen::FPSLimiterMode ()
674{631{
675 return priv->FPSLimiterMode;632 return priv->scheduler.getFPSLimiterMode ();
676}633}
677634
678void635void
679CompositeScreen::setFPSLimiterMode (CompositeFPSLimiterMode newMode)636CompositeScreen::setFPSLimiterMode (CompositeFPSLimiterMode newMode)
680{637{
681 priv->FPSLimiterMode = newMode;638 priv->scheduler.setFPSLimiterMode (newMode);
682}639}
683640
684void641void
685PrivateCompositeScreen::scheduleRepaint ()642PrivateCompositeScreen::prepareScheduledPaint (unsigned int timeDiff)
686{643{
687 if (painting)644 if (pHnd)
688 {645 pHnd->prepareDrawing ();
689 reschedule = true;646
690 return;647 cScreen->preparePaint (slowAnimations ? 1 : timeDiff);
691 }648}
692649
693 if (scheduled)650bool
694 return;651PrivateCompositeScreen::schedulerCompositingActive ()
695652{
696 scheduled = true;653 return pHnd ? pHnd->compositingActive () : false;
697654}
698 int delay;655
699 if (FPSLimiterMode == CompositeFPSLimiterModeVSyncLike ||656bool
700 (pHnd && pHnd->hasVSync ()))657PrivateCompositeScreen::schedulerHasVsync ()
701 {658{
702 delay = 1;659 return pHnd ? pHnd->hasVSync () : false;
660}
661
662void
663PrivateCompositeScreen::paintScheduledPaint ()
664{
665 int mask;
666
667 /* substract top most overlay window region */
668 if (overlayWindowCount)
669 {
670 for (CompWindowList::reverse_iterator rit =
671 screen->windows ().rbegin ();
672 rit != screen->windows ().rend (); rit++)
673 {
674 CompWindow *w = (*rit);
675
676 if (w->destroyed () || w->invisible ())
677 continue;
678
679 if (!CompositeWindow::get (w)->redirected ())
680 damage -= w->region ();
681
682 break;
683 }
684
685 if (damageMask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
686 {
687 damageMask &= ~COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
688 damageMask |= COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
689 }
690 }
691
692 tmpRegion = damage & screen->region ();
693
694 if (damageMask & COMPOSITE_SCREEN_DAMAGE_REGION_MASK)
695 {
696 if (tmpRegion == screen->region ())
697 cScreen->damageScreen ();
698 }
699
700 mask = damageMask;
701
702 damage = CompRegion ();
703 damageMask = 0;
704
705 CompOutput::ptrList outputs (0);
706
707 if (optionGetForceIndependentOutputPainting () ||
708 !screen->hasOverlappingOutputs ())
709 {
710 foreach (CompOutput &o, screen->outputDevs ())
711 outputs.push_back (&o);
703 }712 }
704 else713 else
714 outputs.push_back (&screen->fullscreenOutput ());
715
716 cScreen->paint (outputs, mask);
717
718 damageMask = mask;
719}
720
721bool
722PrivateCompositeScreen::syncScheduledPaint ()
723{
724 if (pHnd)
725 return pHnd->waitVSync (damageMask);
726
727 return false;
728}
729
730void
731PrivateCompositeScreen::doneScheduledPaint ()
732{
733 if (pHnd)
734 pHnd->syncBuffers (damageMask);
735
736 damageMask = 0;
737
738 cScreen->donePaint ();
739
740 foreach (CompWindow *w, screen->windows ())
705 {741 {
706 struct timeval now;742 if (w->destroyed ())
707 gettimeofday (&now, 0);743 {
708 int elapsed = compiz::core::timer::timeval_diff (&now, &lastRedraw);744 CompositeWindow::get (w)->addDamage ();
709 if (elapsed < 0)745 break;
710 elapsed = 0;746 }
711 delay = elapsed < optimalRedrawTime ? optimalRedrawTime - elapsed : 1;
712 }747 }
713
714 paintTimer.start
715 (boost::bind (&CompositeScreen::handlePaintTimeout, cScreen),
716 delay);
717}748}
718749
719int750int
720CompositeScreen::redrawTime ()751CompositeScreen::redrawTime ()
721{752{
722 return priv->redrawTime;753 return priv->scheduler.getRedrawTime ();
723}754}
724755
725int756int
726CompositeScreen::optimalRedrawTime ()757CompositeScreen::optimalRedrawTime ()
727{758{
728 return priv->optimalRedrawTime;759 return priv->scheduler.getOptimalRedrawTime ();
729}
730
731bool
732CompositeScreen::handlePaintTimeout ()
733{
734 struct timeval tv;
735
736 priv->painting = true;
737 priv->reschedule = false;
738 gettimeofday (&tv, 0);
739
740 if (priv->damageMask)
741 {
742 int timeDiff;
743
744 if (priv->pHnd)
745 priv->pHnd->prepareDrawing ();
746
747 timeDiff = compiz::core::timer::timeval_diff (&tv, &priv->lastRedraw);
748
749 /* handle clock rollback */
750 if (timeDiff < 0)
751 timeDiff = 0;
752 /*
753 * Now that we use a "tickless" timing algorithm, timeDiff could be
754 * very large if the screen is truely idle.
755 * However plugins expect the old behaviour where timeDiff is rarely
756 * larger than the frame rate (optimalRedrawTime).
757 * So enforce this to keep animations timed correctly and smooth...
758 */
759 if (timeDiff > 100)
760 timeDiff = priv->optimalRedrawTime;
761
762 priv->redrawTime = timeDiff;
763 preparePaint (priv->slowAnimations ? 1 : timeDiff);
764
765 /* substract top most overlay window region */
766 if (priv->overlayWindowCount)
767 {
768 for (CompWindowList::reverse_iterator rit =
769 screen->windows ().rbegin ();
770 rit != screen->windows ().rend (); rit++)
771 {
772 CompWindow *w = (*rit);
773
774 if (w->destroyed () || w->invisible ())
775 continue;
776
777 if (!CompositeWindow::get (w)->redirected ())
778 priv->damage -= w->region ();
779
780 break;
781 }
782
783 if (priv->damageMask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
784 {
785 priv->damageMask &= ~COMPOSITE_SCREEN_DAMAGE_ALL_MASK;
786 priv->damageMask |= COMPOSITE_SCREEN_DAMAGE_REGION_MASK;
787 }
788 }
789
790 priv->tmpRegion = priv->damage & screen->region ();
791
792 if (priv->damageMask & COMPOSITE_SCREEN_DAMAGE_REGION_MASK)
793 {
794 if (priv->tmpRegion == screen->region ())
795 damageScreen ();
796 }
797
798 priv->damage = CompRegion ();
799
800 int mask = priv->damageMask;
801 priv->damageMask = 0;
802
803 CompOutput::ptrList outputs (0);
804
805 if (priv->optionGetForceIndependentOutputPainting ()
806 || !screen->hasOverlappingOutputs ())
807 {
808 foreach (CompOutput &o, screen->outputDevs ())
809 outputs.push_back (&o);
810 }
811 else
812 outputs.push_back (&screen->fullscreenOutput ());
813
814 paint (outputs, mask);
815
816
817 donePaint ();
818
819 foreach (CompWindow *w, screen->windows ())
820 {
821 if (w->destroyed ())
822 {
823 CompositeWindow::get (w)->addDamage ();
824 break;
825 }
826 }
827 }
828
829 priv->lastRedraw = tv;
830 priv->painting = false;
831 priv->scheduled = false;
832 if (priv->reschedule)
833 priv->scheduleRepaint ();
834
835 return false;
836}760}
837761
838void762void
839763
=== added directory 'plugins/composite/tests'
=== added file 'plugins/composite/tests/CMakeLists.txt'
--- plugins/composite/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/composite/tests/CMakeLists.txt 2012-01-20 14:46:26 +0000
@@ -0,0 +1,1 @@
1add_subdirectory (paintscheduler)
02
=== added directory 'plugins/composite/tests/paintscheduler'
=== added file 'plugins/composite/tests/paintscheduler/CMakeLists.txt'
--- plugins/composite/tests/paintscheduler/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/composite/tests/paintscheduler/CMakeLists.txt 2012-01-20 14:46:26 +0000
@@ -0,0 +1,36 @@
1include (FindPkgConfig)
2
3pkg_check_modules (COMPIZ_PAINTSCHEDULER REQUIRED dri libdrm glibmm-2.4)
4
5find_library( DRM_LIBRARY drm PATHS /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} NO_DEFAULT_PATH )
6
7include_directories (${COMPIZ_INCLUDE_DIRS}
8 ${COMPIZ_PAINTSCHEDULER_INCLUDE_DIRS}
9 ${compiz_SOURCE_DIR}/include
10 ${compiz_BINARY_DIR}
11 ${compiz_BINARY_DIR}/generated
12 ${compiz_SOURCE_DIR}/src
13 ${compiz_SOURCE_DIR}/src/timer/src
14 ${compiz_SOURCE_DIR}/src/timer/include
15 ../../include
16 ../../src)
17
18#message (${DRM_LIBRARY})
19
20# FIXME: Hardcoding drm like this is stupid
21# but due to a bug in CMake we have to do it
22add_executable (compiz_paintscheduler_test
23 ../../src/paintscheduler.cpp
24 ${compiz_SOURCE_DIR}/src/timer/src/timer.cpp
25 ${compiz_SOURCE_DIR}/src/timer/src/timeouthandler.cpp
26 test-paintscheduler.cpp
27 test-paintscheduler-set-drmvblanktype.c)
28
29target_link_libraries (compiz_paintscheduler_test
30 ${COMPIZ_LIBRARIES} m pthread dl ${DRM_LIBRARY})
31
32set_target_properties (compiz_paintscheduler_test PROPERTIES
33 LINK_FLAGS "-L/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}")
34
35add_test (test-paintscheduler
36 ${CMAKE_CURRENT_BINARY_DIR}/compiz_paintscheduler_test)
037
=== added file 'plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c'
--- plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c 1970-01-01 00:00:00 +0000
+++ plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.c 2012-01-20 14:46:26 +0000
@@ -0,0 +1,6 @@
1#include "test-paintscheduler-set-drmvblanktype.h"
2
3void setDRMVBlankType (drmVBlankSeqType *t, unsigned int f)
4{
5 *t = f;
6}
07
=== added file 'plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h'
--- plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h 1970-01-01 00:00:00 +0000
+++ plugins/composite/tests/paintscheduler/test-paintscheduler-set-drmvblanktype.h 2012-01-20 14:46:26 +0000
@@ -0,0 +1,4 @@
1#include <xf86drm.h>
2
3void
4setDRMVBlankType (drmVBlankSeqType *t, unsigned int f);
05
=== added file 'plugins/composite/tests/paintscheduler/test-paintscheduler.cpp'
--- plugins/composite/tests/paintscheduler/test-paintscheduler.cpp 1970-01-01 00:00:00 +0000
+++ plugins/composite/tests/paintscheduler/test-paintscheduler.cpp 2012-01-20 14:46:26 +0000
@@ -0,0 +1,702 @@
1/*
2 * Copyright © 2011 Canonical Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Dennis Kasprzyk not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior permission.
11 * Dennis Kasprzyk makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *
15 * DENNIS KASPRZYK DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL DENNIS KASPRZYK BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * drm_vblank handling code adapted from libdrm
24 * Copyright 2008 Tungsten Graphics
25 * Jakob Bornecrantz <jakob@tungstengraphics.com>
26 * Copyright 2008 Intel Corporation
27 * Jesse Barnes <jesse.barnes@intel.com>
28 *
29 * Authors: Sam Spilsbury <sam.spilsbury@canonical.com>
30 */
31
32#include <iostream>
33#include <string>
34#include <glib.h>
35#include <core/timeouthandler.h>
36#include <paintscheduler.h>
37#include <privatetimeouthandler.h>
38#include <privatetimer.h>
39#include <privatetimeoutsource.h>
40
41#include <assert.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <stdint.h>
45#include <unistd.h>
46#include <string.h>
47#include <errno.h>
48#include <sys/poll.h>
49#include <sys/time.h>
50
51#include <cmath>
52
53extern "C"
54{
55#include "test-paintscheduler-set-drmvblanktype.h"
56}
57#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
58
59using namespace compiz::composite::scheduler;
60
61class DummyPaintDispatch;
62
63void pass (const std::string &message)
64{
65 std::cout << "PASS: " << message << std::endl;
66}
67
68void fail (const std::string &message)
69{
70 std::cout << "FAIL: " << message << std::endl;
71 exit (1);
72}
73
74class Worker
75{
76 public:
77
78 /* Amount of time it takes to complete some work in ms */
79 static void setWorkFactor (unsigned int workFactor) { mWorkFactor = workFactor; }
80 static void work ()
81 {
82 struct timespec ts;
83
84 ts.tv_sec = 0;
85 ts.tv_nsec = 1000000 * mWorkFactor;
86
87 nanosleep (&ts, NULL);
88 }
89
90 private:
91
92 static unsigned int mWorkFactor;
93};
94
95unsigned int Worker::mWorkFactor = 0;
96
97class VBlankWaiter
98{
99 public:
100
101 typedef timeval time_value;
102
103 VBlankWaiter (DummyPaintDispatch *owner, unsigned int n);
104 virtual ~VBlankWaiter ();
105
106 virtual bool waitVBlank () = 0;
107 virtual bool hasVSync () = 0;
108
109 bool checkTimings (float req, float threshold);
110 float averagePeriod ();
111 float averagePhase ();
112
113 bool addPaintStart (unsigned int time);
114 void addPaintDone (const VBlankWaiter::time_value &);
115
116 protected:
117
118 void addVBlank (const VBlankWaiter::time_value &);
119
120 std::vector <float> periods;
121 std::vector <VBlankWaiter::time_value> paintPeriods;
122 std::vector <VBlankWaiter::time_value> blankPeriods;
123 unsigned int timingCount;
124 unsigned int paintCount;
125 DummyPaintDispatch *owner;
126};
127
128class DRMVBlankWaiter :
129 public VBlankWaiter
130{
131 public:
132
133 DRMVBlankWaiter (DummyPaintDispatch *owner, unsigned int n);
134 ~DRMVBlankWaiter ();
135
136 bool waitVBlank ();
137 bool hasVSync ();
138
139 class DRMInfo
140 {
141 public:
142
143 DRMInfo (DRMVBlankWaiter *w) :
144 self (w)
145 {
146 }
147
148 DRMVBlankWaiter *self;
149 };
150
151 protected:
152
153 static gpointer
154 drmEventHandlerThread (gpointer);
155
156 static void
157 vblankHandler (int fd,
158 unsigned int frame,
159 unsigned int sec,
160 unsigned int usec,
161 void *data);
162 private:
163
164 drmVBlank mVbl;
165 drmEventContext evctx;
166 int drmFD;
167 DRMInfo *info;
168 GThread *eventThread;
169 GMutex eventMutex;
170 GCond eventCondition;
171 bool pendingVBlank;
172};
173
174class SleepVBlankWaiter :
175 public VBlankWaiter
176{
177 public:
178
179 SleepVBlankWaiter (DummyPaintDispatch *, unsigned int);
180 ~SleepVBlankWaiter ();
181
182 bool waitVBlank ();
183 bool hasVSync () { return true; }
184
185 static VBlankWaiter::time_value start_time;
186
187 private:
188
189 const static int vblank_ms_period = 60;
190};
191
192VBlankWaiter::time_value SleepVBlankWaiter::start_time;
193
194class NilVBlankWaiter :
195 public VBlankWaiter
196{
197 public:
198
199 NilVBlankWaiter (DummyPaintDispatch *, unsigned int);
200 ~NilVBlankWaiter ();
201
202 bool waitVBlank ();
203
204 bool hasVSync () { return false; }
205};
206
207class DummyPaintDispatch :
208 public PaintSchedulerDispatchBase
209{
210 public:
211
212 DummyPaintDispatch (Glib::RefPtr <Glib::MainLoop> loop);
213
214 void prepareScheduledPaint (unsigned int timeDiff)
215 {
216 success = waiter->addPaintStart (timeDiff);
217 }
218
219 void paintScheduledPaint ()
220 {
221 Worker::work ();
222 }
223
224 bool syncScheduledPaint ()
225 {
226 if (waiter && !isDone ())
227 return waiter->waitVBlank ();
228 else
229 return false;
230 }
231
232 void doneScheduledPaint ()
233 {
234 VBlankWaiter::time_value tv;
235 gettimeofday (&tv, NULL);
236
237 waiter->addPaintDone (tv);
238
239 if (waiter && success)
240 sched.schedule ();
241 else
242 ml->quit ();
243 }
244
245 bool schedulerCompositingActive ()
246 {
247 return true;
248 }
249
250 bool schedulerHasVsync ()
251 {
252 if (isDone ())
253 return false;
254
255 if (waiter)
256 return waiter->hasVSync ();
257
258 return true;
259 }
260
261 void setWaiter (VBlankWaiter *w)
262 {
263 if (waiter)
264 delete waiter;
265
266 waiter = w;
267 }
268
269 void setRefreshRate (unsigned int r)
270 {
271 sched.setRefreshRate (r);
272 }
273
274 bool isDone () { return !(waiter && success); }
275
276 private:
277
278 PaintScheduler sched;
279 VBlankWaiter *waiter;
280 bool success;
281 Glib::RefPtr <Glib::MainLoop> ml;
282};
283
284float
285VBlankWaiter::averagePeriod ()
286{
287 float buf = 0.0f;
288
289 for (std::vector <float>::iterator it = periods.begin ();
290 it != periods.end (); it++)
291 {
292 buf += (*it);
293 }
294
295 buf = buf / (periods.size ());
296
297 return buf;
298}
299
300/* Returns false if a value exists outside
301 * the threshold */
302bool
303VBlankWaiter::checkTimings (float req, float threshold)
304{
305 std::sort (periods.begin (), periods.end ());
306
307 float median = periods[periods.size () / 2];
308 std::vector <float> deviationPeriods (periods.size ());
309
310 unsigned int i = 0;
311
312 for (std::vector <float>::iterator it = periods.begin ();
313 it != periods.end (); it++, i++)
314 deviationPeriods[i] = fabsf ((*it) - median);
315
316 std::sort (deviationPeriods.begin (), deviationPeriods.end ());
317
318 float medianDeviation = deviationPeriods[deviationPeriods.size () / 2];
319
320 std::cout << "DEBUG: median absolute deviation of the periods was " << medianDeviation << std::endl;
321
322 if (medianDeviation > threshold)
323 return false;
324
325 /* No phase checks if there is no vblanking */
326 if (hasVSync ())
327 {
328
329 /* The phase needs to be sensitive to outliers
330 * so use the standard deviation with an acceptable
331 * deviation of 1 */
332
333 std::vector <VBlankWaiter::time_value>::iterator pit = paintPeriods.begin ();
334 std::vector <VBlankWaiter::time_value>::iterator vit = blankPeriods.begin ();
335 float sum = 0.0f;
336 float mean = 0.0f;
337
338 /* Check if any blank periods fall outside of -4ms
339 * of the paint period, that means we're out of phase. */
340
341 for (; vit != blankPeriods.end (); vit++, pit++)
342 sum += ((pit->tv_usec - vit->tv_usec) / 1000.0f);
343
344 mean = sum / blankPeriods.size ();
345
346 pit = paintPeriods.begin ();
347 vit = blankPeriods.begin ();
348
349 sum = 0.0f;
350
351 for (; vit != blankPeriods.end (); vit++, pit++)
352 sum += pow (((pit->tv_usec - vit->tv_usec) / 1000.0f) - mean, 2);
353
354 sum /= blankPeriods.size ();
355
356 std::cout << "DEBUG: st. dev of the phases was " << sqrt (sum) << std::endl;
357
358 if (sqrt (sum) > 0.025f)
359 return false;
360 }
361
362 return true;
363}
364
365bool
366VBlankWaiter::addPaintStart (unsigned int time)
367{
368 if (timingCount < periods.size ())
369 periods[timingCount]= time;
370 else
371 return false;
372
373 ++timingCount;
374
375 return true;
376}
377
378void
379VBlankWaiter::addPaintDone (const VBlankWaiter::time_value &tv)
380{
381 if (paintCount < paintPeriods.size ())
382 paintPeriods[paintCount] = tv;
383 ++paintCount;
384}
385
386void
387VBlankWaiter::addVBlank (const VBlankWaiter::time_value &tv)
388{
389 /* There may be multiple vblanks per paint
390 * because of throttling, so use the last slot
391 * available for the paint period */
392 if (paintCount < blankPeriods.size ())
393 blankPeriods[paintCount] = tv;
394}
395
396VBlankWaiter::VBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
397 periods (n),
398 paintPeriods (n),
399 blankPeriods (n),
400 timingCount (0),
401 paintCount (0),
402 owner (o)
403{
404}
405
406VBlankWaiter::~VBlankWaiter ()
407{
408}
409
410SleepVBlankWaiter::SleepVBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
411 VBlankWaiter (o, n)
412{
413}
414
415SleepVBlankWaiter::~SleepVBlankWaiter ()
416{
417}
418
419bool
420SleepVBlankWaiter::waitVBlank ()
421{
422 /* Wait until the next interval of 16ms */
423 VBlankWaiter::time_value tv;
424 gettimeofday (&tv, NULL);
425
426 unsigned long usecIntervalWait = 16000 - (tv.tv_usec % 16000);
427
428 struct timespec slp;
429
430 slp.tv_sec = 0;
431 slp.tv_nsec = usecIntervalWait * 1000;
432
433 nanosleep (&slp, NULL);
434
435 gettimeofday (&tv, NULL);
436
437 addVBlank (tv);
438
439 return timingCount < periods.size ();
440}
441
442void
443DRMVBlankWaiter::vblankHandler (int fd, unsigned int frame, unsigned int sec, unsigned int usc, void *data)
444{
445 drmVBlank vbl;
446 DRMInfo *info = static_cast <DRMInfo *> (data);
447
448 setDRMVBlankType (&vbl.request.type, DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
449 vbl.request.sequence = 1;
450 vbl.request.signal = (unsigned long) data;
451
452 drmWaitVBlank (fd, &vbl);
453
454 g_mutex_lock (&info->self->eventMutex);
455
456 if (info->self->timingCount != info->self->periods.size ())
457 info->self->pendingVBlank = true;
458
459 g_cond_signal (&info->self->eventCondition);
460 g_mutex_unlock (&info->self->eventMutex);
461}
462
463gpointer
464DRMVBlankWaiter::drmEventHandlerThread (gpointer data)
465{
466 DRMVBlankWaiter *self = static_cast <DRMVBlankWaiter *> (data);
467
468 while (!self->owner->isDone ())
469 {
470 int ret;
471 struct pollfd pfd;
472
473 pfd.fd = self->drmFD;
474 pfd.events = POLLIN | POLLHUP;
475 pfd.revents = 0;
476
477 ret = poll (&pfd, 1, -1);
478
479 if (ret <= 0)
480 {
481 std::cout << "DEBUG: poll () failed" << std::endl;
482 perror ("poll");
483 return NULL;
484 }
485
486 ret = drmHandleEvent (self->drmFD, &self->evctx);
487 if (ret != 0)
488 {
489 drmError (ret, "DRMVBlankWaiter::setupEventContextThread");
490 return NULL;
491 }
492 }
493
494 return NULL;
495}
496
497DRMVBlankWaiter::DRMVBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
498 VBlankWaiter (o, n),
499 info (new DRMInfo (this)),
500 pendingVBlank (false)
501{
502 const char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx" };
503
504 g_mutex_init (&eventMutex);
505 g_cond_init (&eventCondition);
506
507 for (unsigned int i = 0; i < ARRAY_SIZE (modules); i++)
508 {
509 std::cout << "DEBUG: attempting to load platform " << modules[i];
510 drmFD = drmOpen (modules[i], NULL);
511
512 if (drmFD < 0)
513 std::cout << " ... failed" << std::endl;
514 else
515 {
516 std::cout << " ... success" << std::endl;;
517 break;
518 }
519 }
520
521 if (drmFD < 0)
522 throw std::exception ();
523
524 memset (&mVbl, 0, sizeof (drmVBlank));
525 mVbl.request.type = DRM_VBLANK_RELATIVE;
526 mVbl.request.sequence = 0;
527
528 int ret = drmWaitVBlank (drmFD, &mVbl);
529
530 if (ret != 0)
531 {
532 drmError (ret, "DRMVBlankWaiter::DRMVBlankWaiter (RELATIVE)");
533 throw std::exception ();
534 }
535
536 setDRMVBlankType (&mVbl.request.type, DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
537 mVbl.request.sequence = 1;
538 mVbl.request.signal = (unsigned long) info;
539
540 ret = drmWaitVBlank (drmFD, &mVbl);
541
542 if (ret != 0)
543 {
544 drmError (ret, "DRMVBlankWaiter::DRMVBlankWaiter (RELATIVE EVENT)");
545 throw std::exception ();
546 }
547
548 memset (&evctx, 0, sizeof (drmEventContext));
549 evctx.version = DRM_EVENT_CONTEXT_VERSION;
550 evctx.vblank_handler = DRMVBlankWaiter::vblankHandler;
551 evctx.page_flip_handler = NULL;
552
553 eventThread = g_thread_try_new ("drm_wait_t", &DRMVBlankWaiter::drmEventHandlerThread, (void *) this, NULL);
554
555 if (!eventThread)
556 {
557 std::cout << "DEBUG: could not create thread" << std::endl;
558 throw std::exception ();
559 }
560}
561
562DRMVBlankWaiter::~DRMVBlankWaiter ()
563{
564 g_thread_join (eventThread);
565
566 delete info;
567
568 drmClose (drmFD);
569}
570
571NilVBlankWaiter::NilVBlankWaiter (DummyPaintDispatch *o, unsigned int n) :
572 VBlankWaiter (o, n)
573{
574}
575
576bool
577NilVBlankWaiter::waitVBlank ()
578{
579 /* Just add a new "vblank" */
580 VBlankWaiter::time_value tv;
581 gettimeofday (&tv, NULL);
582
583 addVBlank (tv);
584
585 return timingCount < periods.size ();
586}
587
588
589NilVBlankWaiter::~NilVBlankWaiter ()
590{
591}
592
593bool
594DRMVBlankWaiter::hasVSync ()
595{
596 return true;
597}
598
599bool
600DRMVBlankWaiter::waitVBlank ()
601{
602 VBlankWaiter::time_value tv;
603
604 g_mutex_lock (&eventMutex);
605 if (!pendingVBlank && !owner->isDone ())
606 g_cond_wait (&eventCondition, &eventMutex);
607
608 pendingVBlank = false;
609 g_mutex_unlock (&eventMutex);
610
611 gettimeofday (&tv, NULL);
612
613 addVBlank (tv);
614
615 return true;
616}
617
618DummyPaintDispatch::DummyPaintDispatch (Glib::RefPtr <Glib::MainLoop> mainloop) :
619 sched (this),
620 waiter (NULL),
621 success (true),
622 ml (mainloop)
623{
624 sched.schedule ();
625}
626
627bool doTest (const std::string &testName, int refreshRate, float workFactor, bool vsync)
628{
629 TimeoutHandler *th = new TimeoutHandler ();
630 TimeoutHandler::SetDefault (th);
631
632 Glib::RefPtr <Glib::MainContext> ctx = Glib::MainContext::get_default ();
633 Glib::RefPtr <Glib::MainLoop> mainloop = Glib::MainLoop::create (ctx, false);
634 Glib::RefPtr <CompTimeoutSource> timeout = CompTimeoutSource::create (ctx);
635
636 DummyPaintDispatch *dpb = new DummyPaintDispatch (mainloop);
637 VBlankWaiter *vbwaiter = NULL;
638
639 if (vsync)
640 {
641 try
642 {
643 vbwaiter = new DRMVBlankWaiter (dpb, 200);
644 }
645 catch (std::exception &e)
646 {
647 std::cout << "WARN: can't test DRM vblanking! Using hardcoded nanosleep () based waiter" << std::endl;
648 vbwaiter = new SleepVBlankWaiter (dpb, 200);
649 }
650 }
651 else
652 vbwaiter = new NilVBlankWaiter (dpb, 200);
653
654 dpb->setWaiter (vbwaiter);
655 dpb->setRefreshRate (refreshRate);
656
657 Worker::setWorkFactor ((1000 / refreshRate) * workFactor);
658
659 std::cout << "INFO: " << testName << " with refresh rate of " << refreshRate << "Hz and work factor of " << workFactor << (vsync ? " with vertical sync" : " without vertical sync") << std::endl;
660
661 mainloop->run ();
662
663 float averagePeriod = vbwaiter->averagePeriod ();
664
665 std::cout << "DEBUG: average vblank wait time was " << averagePeriod << std::endl;
666 std::cout << "DEBUG: average frame rate " << 1000 / averagePeriod << " Hz" << std::endl;
667 std::cout << "TEST: " << testName << " time " << averagePeriod << " within threshold of " << 10.0f << std::endl;
668
669 if (vbwaiter->checkTimings (averagePeriod, 10.0f) &&
670 1000 / averagePeriod < refreshRate)
671 pass (testName);
672 else
673 fail (testName);
674
675 dpb->setWaiter (NULL);
676
677 delete dpb;
678 delete th;
679
680 return true;
681}
682
683int main (void)
684{
685 gettimeofday (&SleepVBlankWaiter::start_time, NULL);
686
687 doTest ("unthrottled vblank timings", 60, 0.0f, true);
688 doTest ("no vsync 60 Hz refresh rate", 60, 0.0f, false);
689 doTest ("unthrottled vblank timings", 60, 0.8f, true);
690 doTest ("no vsync 60 Hz refresh rate", 60, 0.8f, false);
691 doTest ("vsync 50 Hz refresh rate", 50, 0.0f, true);
692 doTest ("no vsync 20 Hz refresh rate", 30, 0.0f,false);
693 doTest ("vsync 50 Hz refresh rate", 50, 1.6f, true);
694 doTest ("no vsync 20 Hz refresh rate", 30, 1.6f,false);
695 doTest ("vsync 30 Hz refresh rate", 30, 0.0f, true);
696 doTest ("no vsync 100 Hz refresh rate", 100, 0.0f, false);
697 doTest ("vsync 100 Hz refresh rate", 100, 0.0f, true);
698 doTest ("no vsync 100 Hz refresh rate", 100, 2.8f, false);
699 doTest ("vsync 100 Hz refresh rate", 100, 2.8f, true);
700
701 return 0;
702}
0703
=== modified file 'plugins/opengl/include/opengl/opengl.h'
--- plugins/opengl/include/opengl/opengl.h 2012-01-12 06:48:58 +0000
+++ plugins/opengl/include/opengl/opengl.h 2012-01-20 14:46:26 +0000
@@ -102,6 +102,8 @@
102 typedef int (*GLXWaitVideoSyncProc) (int divisor,102 typedef int (*GLXWaitVideoSyncProc) (int divisor,
103 int remainder,103 int remainder,
104 unsigned int *count);104 unsigned int *count);
105 typedef int (*GLXSwapIntervalProc) (int interval);
106
105107
106 #ifndef GLX_VERSION_1_3108 #ifndef GLX_VERSION_1_3
107 typedef struct __GLXFBConfigRec *GLXFBConfig;109 typedef struct __GLXFBConfigRec *GLXFBConfig;
@@ -165,6 +167,7 @@
165 extern GLXCopySubBufferProc copySubBuffer;167 extern GLXCopySubBufferProc copySubBuffer;
166 extern GLXGetVideoSyncProc getVideoSync;168 extern GLXGetVideoSyncProc getVideoSync;
167 extern GLXWaitVideoSyncProc waitVideoSync;169 extern GLXWaitVideoSyncProc waitVideoSync;
170 extern GLXSwapIntervalProc swapInterval;
168 extern GLXGetFBConfigsProc getFBConfigs;171 extern GLXGetFBConfigsProc getFBConfigs;
169 extern GLXGetFBConfigAttribProc getFBConfigAttrib;172 extern GLXGetFBConfigAttribProc getFBConfigAttrib;
170 extern GLXCreatePixmapProc createPixmap;173 extern GLXCreatePixmapProc createPixmap;
171174
=== modified file 'plugins/opengl/src/privates.h'
--- plugins/opengl/src/privates.h 2012-01-19 18:12:31 +0000
+++ plugins/opengl/src/privates.h 2012-01-20 14:46:26 +0000
@@ -67,6 +67,9 @@
67 const CompRegion &region);67 const CompRegion &region);
6868
69 bool hasVSync ();69 bool hasVSync ();
70 bool waitVSync (unsigned int mask);
71
72 void syncBuffers (unsigned int mask);
7073
71 void prepareDrawing ();74 void prepareDrawing ();
7275
@@ -115,6 +118,7 @@
115 GLXContext ctx;118 GLXContext ctx;
116119
117 CompRegion outputRegion;120 CompRegion outputRegion;
121 CompRegion tmpRegion;
118122
119 bool pendingCommands;123 bool pendingCommands;
120124
121125
=== modified file 'plugins/opengl/src/screen.cpp'
--- plugins/opengl/src/screen.cpp 2012-01-12 06:48:58 +0000
+++ plugins/opengl/src/screen.cpp 2012-01-20 14:46:26 +0000
@@ -31,15 +31,13 @@
31#include <math.h>31#include <math.h>
3232
33namespace GL {33namespace GL {
34 typedef int (*GLXSwapIntervalProc) (int interval);
35
36 GLXSwapIntervalProc swapInterval = NULL;
37 GLXBindTexImageProc bindTexImage = NULL;34 GLXBindTexImageProc bindTexImage = NULL;
38 GLXReleaseTexImageProc releaseTexImage = NULL;35 GLXReleaseTexImageProc releaseTexImage = NULL;
39 GLXQueryDrawableProc queryDrawable = NULL;36 GLXQueryDrawableProc queryDrawable = NULL;
40 GLXCopySubBufferProc copySubBuffer = NULL;37 GLXCopySubBufferProc copySubBuffer = NULL;
41 GLXGetVideoSyncProc getVideoSync = NULL;38 GLXGetVideoSyncProc getVideoSync = NULL;
42 GLXWaitVideoSyncProc waitVideoSync = NULL;39 GLXWaitVideoSyncProc waitVideoSync = NULL;
40 GLXSwapIntervalProc swapInterval = NULL;
43 GLXGetFBConfigsProc getFBConfigs = NULL;41 GLXGetFBConfigsProc getFBConfigs = NULL;
44 GLXGetFBConfigAttribProc getFBConfigAttrib = NULL;42 GLXGetFBConfigAttribProc getFBConfigAttrib = NULL;
45 GLXCreatePixmapProc createPixmap = NULL;43 GLXCreatePixmapProc createPixmap = NULL;
@@ -1081,6 +1079,7 @@
1081waitForVideoSync ()1079waitForVideoSync ()
1082{1080{
1083 GL::unthrottledFrames++;1081 GL::unthrottledFrames++;
1082 // Docs: http://www.opengl.org/registry/specs/SGI/video_sync.txt
1084 if (GL::waitVideoSync)1083 if (GL::waitVideoSync)
1085 {1084 {
1086 // Don't wait twice. Just in case.1085 // Don't wait twice. Just in case.
@@ -1122,13 +1121,6 @@
1122} // namespace GL1121} // namespace GL
11231122
1124void1123void
1125PrivateGLScreen::waitForVideoSync ()
1126{
1127 if (optionGetSyncToVblank ())
1128 GL::waitForVideoSync ();
1129}
1130
1131void
1132PrivateGLScreen::paintOutputs (CompOutput::ptrList &outputs,1124PrivateGLScreen::paintOutputs (CompOutput::ptrList &outputs,
1133 unsigned int mask,1125 unsigned int mask,
1134 const CompRegion &region)1126 const CompRegion &region)
@@ -1141,7 +1133,7 @@
1141 glClear (GL_COLOR_BUFFER_BIT);1133 glClear (GL_COLOR_BUFFER_BIT);
1142 }1134 }
11431135
1144 CompRegion tmpRegion (region);1136 tmpRegion = region;
11451137
1146 foreach (CompOutput *output, outputs)1138 foreach (CompOutput *output, outputs)
1147 {1139 {
@@ -1196,7 +1188,11 @@
1196 }1188 }
11971189
1198 targetOutput = &screen->outputDevs ()[0];1190 targetOutput = &screen->outputDevs ()[0];
1191}
11991192
1193bool
1194PrivateGLScreen::waitVSync (unsigned int mask)
1195{
1200 if (mask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)1196 if (mask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
1201 {1197 {
1202 /*1198 /*
@@ -1205,16 +1201,27 @@
1205 * Unfortunately it only works with glXSwapBuffers in most drivers.1201 * Unfortunately it only works with glXSwapBuffers in most drivers.
1206 */1202 */
1207 GL::controlSwapVideoSync (optionGetSyncToVblank ());1203 GL::controlSwapVideoSync (optionGetSyncToVblank ());
1204 return false;
1205 }
1206 else
1207 {
1208 GL::waitForVideoSync ();
1209 return true;
1210 }
1211}
1212
1213void
1214PrivateGLScreen::syncBuffers (unsigned int mask)
1215{
1216 if (mask & COMPOSITE_SCREEN_DAMAGE_ALL_MASK)
1217 {
1208 glXSwapBuffers (screen->dpy (), cScreen->output ());1218 glXSwapBuffers (screen->dpy (), cScreen->output ());
1209 }1219 }
1210 else1220 else
1211 {1221 {
1212 BoxPtr pBox;1222 BoxPtr pBox = const_cast <Region> (tmpRegion.handle ())->rects;
1213 int nBox, y;1223 int nBox = const_cast <Region> (tmpRegion.handle ())->numRects;
12141224 int y;
1215 waitForVideoSync ();
1216 pBox = const_cast <Region> (tmpRegion.handle ())->rects;
1217 nBox = const_cast <Region> (tmpRegion.handle ())->numRects;
12181225
1219 if (GL::copySubBuffer)1226 if (GL::copySubBuffer)
1220 {1227 {
12211228
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2012-01-18 17:05:12 +0000
+++ src/CMakeLists.txt 2012-01-20 14:46:26 +0000
@@ -59,7 +59,7 @@
59 ${CORE_MOD_LIBRARY_DIRS}59 ${CORE_MOD_LIBRARY_DIRS}
60)60)
6161
62add_library (compiz_core62add_library (compiz_core SHARED
63 ${CMAKE_CURRENT_SOURCE_DIR}/global.cpp63 ${CMAKE_CURRENT_SOURCE_DIR}/global.cpp
64 ${CMAKE_CURRENT_SOURCE_DIR}/region.cpp64 ${CMAKE_CURRENT_SOURCE_DIR}/region.cpp
65 ${CMAKE_CURRENT_SOURCE_DIR}/atoms.cpp65 ${CMAKE_CURRENT_SOURCE_DIR}/atoms.cpp
@@ -98,7 +98,7 @@
98 PROPERTY CORE_MOD_LIBRARIES)98 PROPERTY CORE_MOD_LIBRARIES)
9999
100target_link_libraries (100target_link_libraries (
101 compiz_core 101 compiz_core
102 102
103 ${COMPIZ_LIBRARIES} 103 ${COMPIZ_LIBRARIES}
104 104
@@ -115,6 +115,7 @@
115target_link_libraries (115target_link_libraries (
116 compiz 116 compiz
117 compiz_core117 compiz_core
118
118 ${COMPIZ_LIBRARIES} 119 ${COMPIZ_LIBRARIES}
119 120
120 m 121 m
@@ -128,6 +129,13 @@
128)129)
129130
130install (131install (
132 TARGETS compiz_core
133 RUNTIME DESTINATION ${COMPIZ_DESTDIR}${exec_prefix}
134 LIBRARY DESTINATION ${COMPIZ_DESTDIR}${libdir}
135 ARCHIVE DESTINATION ${COMPIZ_DESTDIR}${libdir}
136)
137
138install (
131 TARGETS compiz139 TARGETS compiz
132 DESTINATION ${COMPIZ_DESTDIR}${exec_prefix}140 DESTINATION ${COMPIZ_DESTDIR}${exec_prefix}
133)141)
134142
=== modified file 'src/screen.cpp'
--- src/screen.cpp 2012-01-19 18:12:31 +0000
+++ src/screen.cpp 2012-01-20 14:46:26 +0000
@@ -791,15 +791,27 @@
791 pListCount++;791 pListCount++;
792 }792 }
793793
794 CompString lpName;
795
794 foreach (CompOption::Value &lp, list)796 foreach (CompOption::Value &lp, list)
795 {797 {
796 bool skip = false;798 bool skip = false;
797 if (lp.s () == "core")799
800 try
801 {
802 lpName = lp.s();
803 } catch(const boost::bad_get&)
804 {
805 lpName.clear();
806 }
807
808
809 if (lpName == "core")
798 continue;810 continue;
799811
800 foreach (CompString &p, initialPlugins)812 foreach (CompString &p, initialPlugins)
801 {813 {
802 if (p == lp.s ())814 if (p == lpName)
803 {815 {
804 skip = true;816 skip = true;
805 break;817 break;
@@ -839,20 +851,39 @@
839 {851 {
840 std::list <CompString>::iterator it = initialPlugins.begin ();852 std::list <CompString>::iterator it = initialPlugins.begin ();
841 bool skip = false;853 bool skip = false;
842 if (opt.s () == "core")854
843 continue;855 try
856 {
857 if (opt.s () == "core")
858 continue;
859 } catch(const boost::bad_get&)
860 {
861 }
844862
845 for (; it != initialPlugins.end (); it++)863 for (; it != initialPlugins.end (); it++)
846 {864 {
847 if ((*it) == opt.s ())865 try
848 {866 {
849 skip = true;867 if ((*it) == opt.s())
850 break;868 {
851 }869 skip = true;
870 break;
871 }
872 } catch (...)
873 {
874 }
852 }875 }
853876
854 if (!skip)877 if (!skip)
855 pList.at (j++).set (opt.s ());878 {
879 try
880 {
881 pList.at (j++).set (opt.s ());
882 } catch(const boost::bad_get&)
883 {
884 j++;
885 }
886 }
856 }887 }
857888
858 assert (j == pList.size ());889 assert (j == pList.size ());
@@ -902,7 +933,13 @@
902933
903 if (p == 0 && !failedPush)934 if (p == 0 && !failedPush)
904 {935 {
905 p = CompPlugin::load (pList[i].s ().c_str ());936 try
937 {
938 p = CompPlugin::load (pList[i].s ().c_str ());
939 } catch (...)
940 {
941 }
942
906 if (p)943 if (p)
907 {944 {
908 if (!CompPlugin::push (p))945 if (!CompPlugin::push (p))

Subscribers

People subscribed via source and target branches