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