Mir

Merge lp:~vanvugt/mir/earlier-release-2 into lp:mir

Proposed by Daniel van Vugt
Status: Merged
Merged at revision: 2891
Proposed branch: lp:~vanvugt/mir/earlier-release-2
Merge into: lp:mir
Diff against target: 309 lines (+252/-0)
4 files modified
src/server/compositor/buffer_queue.cpp (+24/-0)
src/server/compositor/buffer_queue.h (+1/-0)
tests/integration-tests/test_buffer_scheduling.cpp (+115/-0)
tests/unit-tests/compositor/test_buffer_queue.cpp (+112/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/earlier-release-2
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Alan Griffiths Needs Fixing
Kevin DuBois (community) Approve
Review via email: mp+269327@code.launchpad.net

Commit message

Reintroduce very short buffer holds so that clients can get a new
buffer to render to much sooner, and potentially appear much
smoother (LP: #1480164)

"very short" means the compositor only holds the buffer for its
render time (often 1ms or less) rather than the full frame time
(16ms).

This also helps in future work to increase concurrency as the
compositor can now better control where in its render loop the
buffer reply will occur (LP: #1395421).

Description of the change

Compositor report render time results from a relatively fast desktop running 50 instances of egltriangle:

Trunk branch: 4.0ms per frame
This branch : 1.7ms per frame

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

LGTM. I think that the tricky part (single vs multimonitor detection) comes from the fact that the MM code doesn't know how many user id's can exist, so we have to infer after-the-call (of compositor acquire). We could resolve if we handed out the ID's, and then we'd know for sure.

I think the term 'overclocking' is introducing another loaded word into the system... Its really designating a different consumption pattern with different tradeoffs. At any rate, I think I have to update the logic in the nbs multimonitor....

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

+1 to the logic, could the integration tests make more use of the scheduling system? There's some rigging already for blocking detection, and the tests will be slightly tricky as-is to refactor to get the new buffer semantics under this test.

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

All good points and they were on my mind already.

It really is a prediction algorithm, whether there's a single or multiple compositors. If we get the prediction wrong for a frame or so (second monitor hotplugged?) the client will get a buffer back a little too quickly, but that's really just one frame so not important.

Yes "overclocking" is an overloaded word here. I was looking for a single term to describe the problem and that's the best short description I've found. But it's definitely confusing and not related to physical monitor signal overclocking, which is a thing.

The new integration tests - I've used your new interfaces previously where it was clear how to do so. Sorry in some of this work it's not clear to me how to do so. I imagine some translation will happen later...

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

71 + mtd::MockFrameDroppingPolicyFactory policy_factory;
72 + mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory),
73 + properties, policy_factory};
74 + queue.allow_framedropping(false);
75 +
76 + mg::Buffer* client_buffer = nullptr;
77 + auto callback = [&](mg::Buffer* buffer)
78 + {
79 + client_buffer = buffer;
80 + };
81 +
82 + auto client_try_acquire = [&]() -> bool
83 + {
84 + queue.client_acquire(callback);
85 + return client_buffer != nullptr;
86 + };

C++11 is shiny bit this is being too clever.

Why aren't queue, client_try_acquire etc. just members of a fixture?

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

To keep the diff small. Also it's temporary and will be replaced when BufferQueue goes away, so better to not bake it into a fixture.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/server/compositor/buffer_queue.cpp'
--- src/server/compositor/buffer_queue.cpp 2015-08-24 10:23:21 +0000
+++ src/server/compositor/buffer_queue.cpp 2015-08-27 10:34:15 +0000
@@ -144,6 +144,7 @@
144 the_properties{props},144 the_properties{props},
145 force_new_compositor_buffer{false},145 force_new_compositor_buffer{false},
146 callbacks_allowed{true},146 callbacks_allowed{true},
147 single_compositor{false}, // When true we can optimize performance
147 gralloc{gralloc}148 gralloc{gralloc}
148{149{
149 if (nbuffers < 1)150 if (nbuffers < 1)
@@ -286,6 +287,9 @@
286 current_buffer_users.push_back(user_id);287 current_buffer_users.push_back(user_id);
287 }288 }
288289
290 single_compositor = current_compositor_buffer_valid &&
291 current_buffer_users.size() <= 1; // might be zero
292
289 if (ready_to_composite_queue.empty())293 if (ready_to_composite_queue.empty())
290 {294 {
291 use_current_buffer = true;295 use_current_buffer = true;
@@ -357,6 +361,26 @@
357361
358 if (current_compositor_buffer != buffer.get())362 if (current_compositor_buffer != buffer.get())
359 release(buffer.get(), std::move(lock));363 release(buffer.get(), std::move(lock));
364 else if (!ready_to_composite_queue.empty() && single_compositor)
365 {
366 /*
367 * The "early release" optimisation: Note "single_compositor" above
368 * is because this path will break (actually just overclock) the
369 * multi-monitor frame sync algorithm. For the moment we prefer
370 * /perfect/ multi-monitor frame sync all the time. But if you so
371 * choose that overclocking with multi-monitors is acceptable then
372 * you could remove the above "single_compositor" check and get this
373 * optimised code path with multi-monitors too...
374 */
375 current_compositor_buffer = pop(ready_to_composite_queue);
376 current_buffer_users.clear();
377 /*
378 * As we have now caused the next compositor_acquire() to skip the
379 * frame_deadlines_met update, we need to do it here:
380 */
381 ++frame_deadlines_met;
382 release(buffer.get(), std::move(lock));
383 }
360}384}
361385
362std::shared_ptr<mg::Buffer> mc::BufferQueue::snapshot_acquire()386std::shared_ptr<mg::Buffer> mc::BufferQueue::snapshot_acquire()
363387
=== modified file 'src/server/compositor/buffer_queue.h'
--- src/server/compositor/buffer_queue.h 2015-08-21 02:02:07 +0000
+++ src/server/compositor/buffer_queue.h 2015-08-27 10:34:15 +0000
@@ -114,6 +114,7 @@
114 graphics::BufferProperties the_properties;114 graphics::BufferProperties the_properties;
115 bool force_new_compositor_buffer;115 bool force_new_compositor_buffer;
116 bool callbacks_allowed;116 bool callbacks_allowed;
117 bool single_compositor;
117118
118 std::condition_variable snapshot_released;119 std::condition_variable snapshot_released;
119 std::shared_ptr<graphics::GraphicBufferAllocator> gralloc;120 std::shared_ptr<graphics::GraphicBufferAllocator> gralloc;
120121
=== modified file 'tests/integration-tests/test_buffer_scheduling.cpp'
--- tests/integration-tests/test_buffer_scheduling.cpp 2015-08-26 09:20:44 +0000
+++ tests/integration-tests/test_buffer_scheduling.cpp 2015-08-27 10:34:15 +0000
@@ -714,6 +714,121 @@
714 }714 }
715}715}
716716
717TEST_P(WithTwoOrMoreBuffers, clients_get_new_buffers_on_compositor_release)
718{ // Regression test for LP: #1480164
719 mtd::MockFrameDroppingPolicyFactory policy_factory;
720 mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory),
721 properties, policy_factory};
722 queue.allow_framedropping(false);
723
724 mg::Buffer* client_buffer = nullptr;
725 auto callback = [&](mg::Buffer* buffer)
726 {
727 client_buffer = buffer;
728 };
729
730 auto client_try_acquire = [&]() -> bool
731 {
732 queue.client_acquire(callback);
733 return client_buffer != nullptr;
734 };
735
736 auto client_release = [&]()
737 {
738 EXPECT_TRUE(client_buffer);
739 queue.client_release(client_buffer);
740 client_buffer = nullptr;
741 };
742
743 // Skip over the first frame. The early release optimization is too
744 // conservative to allow it to happen right at the start (so as to
745 // maintain correct multimonitor frame rates if required).
746 ASSERT_TRUE(client_try_acquire());
747 client_release();
748 queue.compositor_release(queue.compositor_acquire(this));
749
750 auto onscreen = queue.compositor_acquire(this);
751
752 bool blocking;
753 do
754 {
755 blocking = !client_try_acquire();
756 if (!blocking)
757 client_release();
758 } while (!blocking);
759
760 for (int f = 0; f < 100; ++f)
761 {
762 ASSERT_FALSE(client_buffer);
763 queue.compositor_release(onscreen);
764 ASSERT_TRUE(client_buffer) << "frame# " << f;
765 client_release();
766 onscreen = queue.compositor_acquire(this);
767 client_try_acquire();
768 }
769}
770
771TEST_P(WithTwoOrMoreBuffers, short_buffer_holds_dont_overclock_multimonitor)
772{ // Regression test related to LP: #1480164
773 mtd::MockFrameDroppingPolicyFactory policy_factory;
774 mc::BufferQueue queue{nbuffers, mt::fake_shared(server_buffer_factory),
775 properties, policy_factory};
776 queue.allow_framedropping(false);
777
778 const void* const leftid = "left";
779 const void* const rightid = "right";
780
781 mg::Buffer* client_buffer = nullptr;
782 auto callback = [&](mg::Buffer* buffer)
783 {
784 client_buffer = buffer;
785 };
786
787 auto client_try_acquire = [&]() -> bool
788 {
789 queue.client_acquire(callback);
790 return client_buffer != nullptr;
791 };
792
793 auto client_release = [&]()
794 {
795 EXPECT_TRUE(client_buffer);
796 queue.client_release(client_buffer);
797 client_buffer = nullptr;
798 };
799
800 // Skip over the first frame. The early release optimization is too
801 // conservative to allow it to happen right at the start (so as to
802 // maintain correct multimonitor frame rates if required).
803 ASSERT_TRUE(client_try_acquire());
804 client_release();
805 queue.compositor_release(queue.compositor_acquire(leftid));
806
807 auto left = queue.compositor_acquire(leftid);
808 auto right = queue.compositor_acquire(rightid);
809
810 bool blocking;
811 do
812 {
813 blocking = !client_try_acquire();
814 if (!blocking)
815 client_release();
816 } while (!blocking);
817
818 for (int f = 0; f < 100; ++f)
819 {
820 ASSERT_FALSE(client_buffer);
821 queue.compositor_release(left);
822 queue.compositor_release(right);
823 ASSERT_FALSE(client_buffer);
824 left = queue.compositor_acquire(leftid);
825 right = queue.compositor_acquire(rightid);
826 ASSERT_TRUE(client_buffer);
827 client_release();
828 client_try_acquire();
829 }
830}
831
717TEST_P(WithAnyNumberOfBuffers, compositor_inflates_ready_count_for_slow_clients)832TEST_P(WithAnyNumberOfBuffers, compositor_inflates_ready_count_for_slow_clients)
718{833{
719 queue.set_scaling_delay(3);834 queue.set_scaling_delay(3);
720835
=== modified file 'tests/unit-tests/compositor/test_buffer_queue.cpp'
--- tests/unit-tests/compositor/test_buffer_queue.cpp 2015-08-24 10:22:40 +0000
+++ tests/unit-tests/compositor/test_buffer_queue.cpp 2015-08-27 10:34:15 +0000
@@ -477,6 +477,118 @@
477 EXPECT_NO_THROW(q.compositor_release(comp_buffer));477 EXPECT_NO_THROW(q.compositor_release(comp_buffer));
478}478}
479479
480TEST_P(WithTwoOrMoreBuffers, clients_get_new_buffers_on_compositor_release)
481{ // Regression test for LP: #1480164
482 q.allow_framedropping(false);
483
484 // Skip over the first frame. The early release optimization is too
485 // conservative to allow it to happen right at the start (so as to
486 // maintain correct multimonitor frame rates if required).
487 auto handle = client_acquire_async(q);
488 ASSERT_TRUE(handle->has_acquired_buffer());
489 handle->release_buffer();
490 q.compositor_release(q.compositor_acquire(this));
491
492 auto onscreen = q.compositor_acquire(this);
493
494 // This is what tests should do instead of using buffers_free_for_client()
495 bool blocking;
496 do
497 {
498 handle = client_acquire_async(q);
499 blocking = !handle->has_acquired_buffer();
500 if (!blocking)
501 handle->release_buffer();
502 } while (!blocking);
503
504 for (int f = 0; f < 100; ++f)
505 {
506 ASSERT_FALSE(handle->has_acquired_buffer());
507 q.compositor_release(onscreen);
508 ASSERT_TRUE(handle->has_acquired_buffer()) << "frame# " << f;
509 handle->release_buffer();
510 onscreen = q.compositor_acquire(this);
511 handle = client_acquire_async(q);
512 }
513}
514
515TEST_P(WithTwoOrMoreBuffers, short_buffer_holds_dont_overclock_multimonitor)
516{ // Regression test related to LP: #1480164
517 q.allow_framedropping(false);
518
519 // Skip over the first frame. The early release optimization is too
520 // conservative to allow it to happen right at the start (so as to
521 // maintain correct multimonitor frame rates if required).
522 auto handle = client_acquire_async(q);
523 ASSERT_TRUE(handle->has_acquired_buffer());
524 handle->release_buffer();
525
526 const void* const leftid = "left";
527 const void* const rightid = "right";
528 auto left = q.compositor_acquire(leftid);
529 q.compositor_release(left);
530 left = q.compositor_acquire(leftid);
531 auto right = q.compositor_acquire(rightid);
532
533 // This is what tests should do instead of using buffers_free_for_client()
534 bool blocking;
535 do
536 {
537 handle = client_acquire_async(q);
538 blocking = !handle->has_acquired_buffer();
539 if (!blocking)
540 handle->release_buffer();
541 } while (!blocking);
542
543 for (int f = 0; f < 100; ++f)
544 {
545 ASSERT_FALSE(handle->has_acquired_buffer());
546 q.compositor_release(left);
547 q.compositor_release(right);
548 ASSERT_FALSE(handle->has_acquired_buffer());
549 left = q.compositor_acquire(leftid);
550 right = q.compositor_acquire(rightid);
551 ASSERT_TRUE(handle->has_acquired_buffer());
552 handle->release_buffer();
553 handle = client_acquire_async(q);
554 }
555}
556
557TEST_P(WithThreeOrMoreBuffers, greedy_clients_get_new_buffers_on_compositor_release)
558{ // Regression test for LP: #1480164
559 q.allow_framedropping(false);
560
561 // Skip over the first frame. The early release optimization is too
562 // conservative to allow it to happen right at the start (so as to
563 // maintain correct multimonitor frame rates if required).
564 auto handle = client_acquire_async(q);
565 ASSERT_TRUE(handle->has_acquired_buffer());
566 handle->release_buffer();
567 q.compositor_release(q.compositor_acquire(this));
568
569 auto onscreen = q.compositor_acquire(this);
570 auto old_handle = handle;
571 old_handle.reset();
572 bool blocking;
573 do
574 {
575 handle = client_acquire_async(q);
576 blocking = !handle->has_acquired_buffer();
577 if (!blocking)
578 {
579 if (old_handle)
580 old_handle->release_buffer();
581 old_handle = handle;
582 handle.reset();
583 }
584 } while (!blocking);
585
586 ASSERT_TRUE(old_handle->has_acquired_buffer());
587 ASSERT_FALSE(handle->has_acquired_buffer());
588 q.compositor_release(onscreen);
589 ASSERT_TRUE(handle->has_acquired_buffer());
590}
591
480TEST_P(WithAnyNumberOfBuffers, multiple_compositors_are_in_sync)592TEST_P(WithAnyNumberOfBuffers, multiple_compositors_are_in_sync)
481{593{
482 auto handle = client_acquire_async(q);594 auto handle = client_acquire_async(q);

Subscribers

People subscribed via source and target branches